Snap for 10453563 from d212ec450807f0029bb95cb37040f9126d751761 to mainline-os-statsd-release

Change-Id: I69a19dc4c0ec47a0483b917c7052f0daa3ee34f0
diff --git a/Android.mk b/Android.mk
index 7f739fb..14b5358 100644
--- a/Android.mk
+++ b/Android.mk
@@ -72,7 +72,7 @@
 	# add in the local wts py files for use with the prebuilt
 	$(hide) zip -r $(WTS_ACTS_DISTRO_ARCHIVE) -j tools/test/connectivity/wts-acts/*.py
 	# create executable tool from the archive
-	$(hide) echo '#!/usr/bin/env python' | cat - $(WTS_ACTS_DISTRO_DIR)/wts-acts.zip > $(WTS_ACTS_DISTRO_DIR)/wts-acts
+	$(hide) echo '#!/usr/bin/env python3' | cat - $(WTS_ACTS_DISTRO_DIR)/wts-acts.zip > $(WTS_ACTS_DISTRO_DIR)/wts-acts
 	$(hide) chmod 755 $(WTS_ACTS_DISTRO)
 
 wts-acts: $(WTS_ACTS_DISTRO)
diff --git a/acts/framework/acts/base_test.py b/acts/framework/acts/base_test.py
index 6bf9424..a3b6546 100755
--- a/acts/framework/acts/base_test.py
+++ b/acts/framework/acts/base_test.py
@@ -101,9 +101,9 @@
     test_instance = event.test_class
     try:
         for fd in getattr(test_instance, 'fuchsia_devices', []):
-            if not fd.skip_sl4f:
-                fd.logging_lib.logI("%s BEGIN %s" %
-                                    (TEST_CASE_TOKEN, event.test_case_name))
+            if hasattr(fd, '_sl4f'):
+                fd.sl4f.logging_lib.logI(
+                    "%s BEGIN %s" % (TEST_CASE_TOKEN, event.test_case_name))
 
     except Exception as e:
         test_instance.log.warning(
@@ -118,9 +118,9 @@
     test_instance = event.test_class
     try:
         for fd in getattr(test_instance, 'fuchsia_devices', []):
-            if not fd.skip_sl4f:
-                fd.logging_lib.logI("%s END %s" %
-                                    (TEST_CASE_TOKEN, event.test_case_name))
+            if hasattr(fd, '_sl4f'):
+                fd.sl4f.logging_lib.logI(
+                    "%s END %s" % (TEST_CASE_TOKEN, event.test_case_name))
 
     except Exception as e:
         test_instance.log.warning(
@@ -405,6 +405,7 @@
             test_name: Name of the test that triggered this function.
             begin_time: Logline format timestamp taken when the test started.
         """
+
     def _on_pass(self, record):
         """Proxy function to guarantee the base implementation of on_pass is
         called.
@@ -429,6 +430,7 @@
             test_name: Name of the test that triggered this function.
             begin_time: Logline format timestamp taken when the test started.
         """
+
     def _on_skip(self, record):
         """Proxy function to guarantee the base implementation of on_skip is
         called.
@@ -450,6 +452,7 @@
             test_name: Name of the test that triggered this function.
             begin_time: Logline format timestamp taken when the test started.
         """
+
     def _on_exception(self, record):
         """Proxy function to guarantee the base implementation of on_exception
         is called.
@@ -471,6 +474,7 @@
             test_name: Name of the test that triggered this function.
             begin_time: Logline format timestamp taken when the test started.
         """
+
     def on_retry(self):
         """Function to run before retrying a test through get_func_with_retry.
 
@@ -478,6 +482,7 @@
         can be used to modify internal test parameters, for example, to retry
         a test with slightly different input variables.
         """
+
     def _exec_procedure_func(self, func, tr_record):
         """Executes a procedure function like on_pass, on_fail etc.
 
@@ -816,8 +821,10 @@
         # Run tests in order.
         test_case_iterations = self.user_params.get(
             keys.Config.key_test_case_iterations.value, 1)
-        if any([substr in self.__class__.__name__ for substr in
-                ['Preflight', 'Postflight']]):
+        if any([
+                substr in self.__class__.__name__
+                for substr in ['Preflight', 'Postflight']
+        ]):
             test_case_iterations = 1
         try:
             for test_name, test_func in tests:
diff --git a/acts/framework/acts/config_parser.py b/acts/framework/acts/config_parser.py
index a639c38..ed2aca5 100755
--- a/acts/framework/acts/config_parser.py
+++ b/acts/framework/acts/config_parser.py
@@ -13,7 +13,6 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-import copy
 import itertools
 import os
 import sys
diff --git a/acts/framework/acts/controllers/access_point.py b/acts/framework/acts/controllers/access_point.py
index f45faf5..687ac94 100755
--- a/acts/framework/acts/controllers/access_point.py
+++ b/acts/framework/acts/controllers/access_point.py
@@ -18,6 +18,7 @@
 import ipaddress
 import os
 import time
+from typing import FrozenSet, Set
 
 from acts import logger
 from acts import utils
@@ -32,6 +33,10 @@
 from acts.controllers.ap_lib import hostapd_ap_preset
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib import hostapd_config
+from acts.controllers.ap_lib import radvd
+from acts.controllers.ap_lib import radvd_config
+from acts.controllers.ap_lib.extended_capabilities import ExtendedCapabilities
+from acts.controllers.ap_lib.wireless_network_management import BssTransitionManagementRequest
 from acts.controllers.utils_lib.commands import ip
 from acts.controllers.utils_lib.commands import route
 from acts.controllers.utils_lib.commands import shell
@@ -88,26 +93,30 @@
     return [ap.ssh_settings.hostname for ap in aps]
 
 
-def setup_ap(access_point,
-             profile_name,
-             channel,
-             ssid,
-             mode=None,
-             preamble=None,
-             beacon_interval=None,
-             dtim_period=None,
-             frag_threshold=None,
-             rts_threshold=None,
-             force_wmm=None,
-             hidden=False,
-             security=None,
-             pmf_support=None,
-             additional_ap_parameters=None,
-             password=None,
-             n_capabilities=None,
-             ac_capabilities=None,
-             vht_bandwidth=None,
-             setup_bridge=False):
+def setup_ap(
+        access_point,
+        profile_name,
+        channel,
+        ssid,
+        mode=None,
+        preamble=None,
+        beacon_interval=None,
+        dtim_period=None,
+        frag_threshold=None,
+        rts_threshold=None,
+        force_wmm=None,
+        hidden=False,
+        security=None,
+        pmf_support=None,
+        additional_ap_parameters=None,
+        password=None,
+        n_capabilities=None,
+        ac_capabilities=None,
+        vht_bandwidth=None,
+        wnm_features: FrozenSet[hostapd_constants.WnmFeature] = frozenset(),
+        setup_bridge=False,
+        is_ipv6_enabled=False,
+        is_nat_enabled=True):
     """Creates a hostapd profile and runs it on an ap. This is a convenience
     function that allows us to start an ap with a single function, without first
     creating a hostapd config.
@@ -128,6 +137,13 @@
         additional_ap_parameters: Additional parameters to send the AP.
         password: Password to connect to WLAN if necessary.
         check_connectivity: Whether to check for internet connectivity.
+        wnm_features: WNM features to enable on the AP.
+        setup_bridge: Whether to bridge the LAN interface WLAN interface.
+            Only one WLAN interface can be bridged with the LAN interface
+            and none of the guest networks can be bridged.
+        is_ipv6_enabled: If True, start a IPv6 router advertisement daemon
+        is_nat_enabled: If True, start NAT on the AP to allow the DUT to be able
+            to access the internet if the WAN port is connected to the internet.
 
     Returns:
         An identifier for each ssid being started. These identifiers can be
@@ -154,10 +170,13 @@
                                             pmf_support=pmf_support,
                                             n_capabilities=n_capabilities,
                                             ac_capabilities=ac_capabilities,
-                                            vht_bandwidth=vht_bandwidth)
+                                            vht_bandwidth=vht_bandwidth,
+                                            wnm_features=wnm_features)
     return access_point.start_ap(
         hostapd_config=ap,
+        radvd_config=radvd_config.RadvdConfig() if is_ipv6_enabled else None,
         setup_bridge=setup_bridge,
+        is_nat_enabled=is_nat_enabled,
         additional_parameters=additional_ap_parameters)
 
 
@@ -192,8 +211,8 @@
             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: f'[Access Point|{self.ssh_settings.hostname}] {msg}')
         self.device_pdu_config = configs.get('PduDevice', None)
         self.identifier = self.ssh_settings.hostname
 
@@ -220,6 +239,7 @@
         self._aps = dict()
         self._dhcp = None
         self._dhcp_bss = dict()
+        self._radvd = None
         self.bridge = bridge_interface.BridgeInterface(self)
         self.iwconfig = ap_iwconfig.ApIwconfig(self)
 
@@ -258,20 +278,22 @@
             self.log.info('No hostapd running')
         # Bring down all wireless interfaces
         for iface in self.wlan:
-            WLAN_DOWN = 'ifconfig {} down'.format(iface)
+            WLAN_DOWN = f'ip link set {iface} down'
             self.ssh.run(WLAN_DOWN)
         # Bring down all bridge interfaces
         bridge_interfaces = self.interfaces.get_bridge_interface()
         if bridge_interfaces:
             for iface in bridge_interfaces:
-                BRIDGE_DOWN = 'ifconfig {} down'.format(iface)
-                BRIDGE_DEL = 'brctl delbr {}'.format(iface)
+                BRIDGE_DOWN = f'ip link set {iface} down'
+                BRIDGE_DEL = f'brctl delbr {iface}'
                 self.ssh.run(BRIDGE_DOWN)
                 self.ssh.run(BRIDGE_DEL)
 
     def start_ap(self,
                  hostapd_config,
+                 radvd_config=None,
                  setup_bridge=False,
+                 is_nat_enabled=True,
                  additional_parameters=None):
         """Starts as an ap using a set of configurations.
 
@@ -284,9 +306,14 @@
         Args:
             hostapd_config: hostapd_config.HostapdConfig, The configurations
                 to use when starting up the ap.
+            radvd_config: radvd_config.RadvdConfig, The IPv6 configuration
+                to use when starting up the ap.
             setup_bridge: Whether to bridge the LAN interface WLAN interface.
                 Only one WLAN interface can be bridged with the LAN interface
                 and none of the guest networks can be bridged.
+            is_nat_enabled: If True, start NAT on the AP to allow the DUT to be
+                able to access the internet if the WAN port is connected to the
+                internet.
             additional_parameters: A dictionary of parameters that can sent
                 directly into the hostapd config file.  This can be used for
                 debugging and or adding one off parameters into the config.
@@ -315,7 +342,7 @@
         # up to 8 different mac addresses. So in for one interface the range is
         # hex 0-7 and for the other the range is hex 8-f.
         interface_mac_orig = None
-        cmd = "ifconfig %s|grep ether|awk -F' ' '{print $2}'" % interface
+        cmd = f"ip link show {interface}|grep ether|awk -F' ' '{{print $2}}'"
         interface_mac_orig = self.ssh.run(cmd)
         if interface == self.wlan_5g:
             hostapd_config.bssid = interface_mac_orig.stdout[:-1] + '0'
@@ -325,7 +352,7 @@
             last_octet = 9
         if interface in self._aps:
             raise ValueError('No WiFi interface available for AP on '
-                             'channel %d' % hostapd_config.channel)
+                             f'channel {hostapd_config.channel}')
 
         apd = hostapd.Hostapd(self.ssh, interface)
         new_instance = _ApInstance(hostapd=apd, subnet=subnet)
@@ -358,8 +385,7 @@
                     starting_ip_range = self._AP_5G_SUBNET_STR
                 a, b, c, d = starting_ip_range.split('.')
                 self._dhcp_bss[bss] = dhcp_config.Subnet(
-                    ipaddress.ip_network('%s.%s.%s.%s' %
-                                         (a, b, str(int(c) + counter), d)))
+                    ipaddress.ip_network(f'{a}.{b}.{int(c) + counter}.{d}'))
                 counter = counter + 1
                 last_octet = last_octet + 1
 
@@ -368,7 +394,7 @@
         # The DHCP serer requires interfaces to have ips and routes before
         # the server will come up.
         interface_ip = ipaddress.ip_interface(
-            '%s/%s' % (subnet.router, subnet.network.netmask))
+            f'{subnet.router}/{subnet.network.netmask}')
         if setup_bridge is True:
             bridge_interface_name = 'eth_test'
             self.create_bridge(bridge_interface_name, [interface, self.lan])
@@ -382,15 +408,24 @@
             # variables represent the interface name, k, and dhcp info, v.
             for k, v in self._dhcp_bss.items():
                 bss_interface_ip = ipaddress.ip_interface(
-                    '%s/%s' % (self._dhcp_bss[k].router,
-                               self._dhcp_bss[k].network.netmask))
+                    f'{self._dhcp_bss[k].router}/{self._dhcp_bss[k].network.netmask}'
+                )
                 self._ip_cmd.set_ipv4_address(str(k), bss_interface_ip)
 
         # Restart the DHCP server with our updated list of subnets.
         configured_subnets = self.get_configured_subnets()
         dhcp_conf = dhcp_config.DhcpConfig(subnets=configured_subnets)
         self.start_dhcp(dhcp_conf=dhcp_conf)
-        self.start_nat()
+        if is_nat_enabled:
+            self.start_nat()
+            self.enable_forwarding()
+        else:
+            self.stop_nat()
+            self.enable_forwarding()
+        if radvd_config:
+            radvd_interface = bridge_interface_name if setup_bridge else interface
+            self._radvd = radvd.Radvd(self.ssh, radvd_interface)
+            self._radvd.start(radvd_config)
 
         bss_interfaces = [bss for bss in hostapd_config.bss_lookup]
         bss_interfaces.append(interface)
@@ -458,6 +493,15 @@
                 identifier).hostapd.pull_logs()
         return hostapd_logs
 
+    def enable_forwarding(self):
+        """Enable IPv4 and IPv6 forwarding on the AP.
+
+        When forwarding is enabled, the access point is able to route IP packets
+        between devices in the same subnet.
+        """
+        self.ssh.run('echo 1 > /proc/sys/net/ipv4/ip_forward')
+        self.ssh.run('echo 1 > /proc/sys/net/ipv6/conf/all/forwarding')
+
     def start_nat(self):
         """Start NAT on the AP.
 
@@ -472,10 +516,8 @@
         # WLAN/LAN ports will be able to access the internet if the WAN port
         # is connected to the internet.
         self.ssh.run('iptables -t nat -F')
-        self.ssh.run('iptables -t nat -A POSTROUTING -o %s -j MASQUERADE' %
-                     self.wan)
-        self.ssh.run('echo 1 > /proc/sys/net/ipv4/ip_forward')
-        self.ssh.run('echo 1 > /proc/sys/net/ipv6/conf/all/forwarding')
+        self.ssh.run(
+            f'iptables -t nat -A POSTROUTING -o {self.wan} -j MASQUERADE')
 
     def stop_nat(self):
         """Stop NAT on the AP.
@@ -487,8 +529,6 @@
         per-interface masquerade rules.
         """
         self.ssh.run('iptables -t nat -F')
-        self.ssh.run('echo 0 > /proc/sys/net/ipv4/ip_forward')
-        self.ssh.run('echo 0 > /proc/sys/net/ipv6/conf/all/forwarding')
 
     def create_bridge(self, bridge_name, interfaces):
         """Create the specified bridge and bridge the specified interfaces.
@@ -499,15 +539,12 @@
         """
 
         # Create the bridge interface
-        self.ssh.run(
-            'brctl addbr {bridge_name}'.format(bridge_name=bridge_name))
+        self.ssh.run(f'brctl addbr {bridge_name}')
 
         for interface in interfaces:
-            self.ssh.run('brctl addif {bridge_name} {interface}'.format(
-                bridge_name=bridge_name, interface=interface))
+            self.ssh.run(f'brctl addif {bridge_name} {interface}')
 
-        self.ssh.run(
-            'ip link set {bridge_name} up'.format(bridge_name=bridge_name))
+        self.ssh.run(f'ip link set {bridge_name} up')
 
     def remove_bridge(self, bridge_name):
         """Removes the specified bridge
@@ -521,17 +558,13 @@
         #
         # Or if we're doing 2.4Ghz and 5Ghz SSIDs and we've already torn
         # down the bridge once, but we got called for each band.
-        result = self.ssh.run(
-            'brctl show {bridge_name}'.format(bridge_name=bridge_name),
-            ignore_status=True)
+        result = self.ssh.run(f'brctl show {bridge_name}', ignore_status=True)
 
         # If the bridge exists, we'll get an exit_status of 0, indicating
         # success, so we can continue and remove the bridge.
         if result.exit_status == 0:
-            self.ssh.run('ip link set {bridge_name} down'.format(
-                bridge_name=bridge_name))
-            self.ssh.run(
-                'brctl delbr {bridge_name}'.format(bridge_name=bridge_name))
+            self.ssh.run(f'ip link set {bridge_name} down')
+            self.ssh.run(f'brctl delbr {bridge_name}')
 
     def get_bssid_from_ssid(self, ssid, band):
         """Gets the BSSID from a provided SSID
@@ -548,18 +581,17 @@
 
         # Get the interface name associated with the given ssid.
         for interface in interfaces:
-            cmd = "iw dev %s info|grep ssid|awk -F' ' '{print $2}'" % (
-                str(interface))
-            iw_output = self.ssh.run(cmd)
+            iw_output = self.ssh.run(
+                f"iw dev {interface} info|grep ssid|awk -F' ' '{{print $2}}'")
             if 'command failed: No such device' in iw_output.stderr:
                 continue
             else:
                 # If the configured ssid is equal to the given ssid, we found
                 # the right interface.
                 if iw_output.stdout == ssid:
-                    cmd = "iw dev %s info|grep addr|awk -F' ' '{print $2}'" % (
-                        str(interface))
-                    iw_output = self.ssh.run(cmd)
+                    iw_output = self.ssh.run(
+                        f"iw dev {interface} info|grep addr|awk -F' ' '{{print $2}}'"
+                    )
                     return iw_output.stdout
         return None
 
@@ -571,23 +603,26 @@
         """
 
         if identifier not in list(self._aps.keys()):
-            raise ValueError('Invalid identifier %s given' % identifier)
+            raise ValueError(f'Invalid identifier {identifier} given')
 
         instance = self._aps.get(identifier)
 
-        instance.hostapd.stop()
+        if self._radvd:
+            self._radvd.stop()
         try:
             self.stop_dhcp()
         except dhcp_server.NoInterfaceError:
             pass
+        self.stop_nat()
+        instance.hostapd.stop()
         self._ip_cmd.clear_ipv4_addresses(identifier)
 
         del self._aps[identifier]
         bridge_interfaces = self.interfaces.get_bridge_interface()
         if bridge_interfaces:
             for iface in bridge_interfaces:
-                BRIDGE_DOWN = 'ifconfig {} down'.format(iface)
-                BRIDGE_DEL = 'brctl delbr {}'.format(iface)
+                BRIDGE_DOWN = f'ip link set {iface} down'
+                BRIDGE_DEL = f'brctl delbr {iface}'
                 self.ssh.run(BRIDGE_DOWN)
                 self.ssh.run(BRIDGE_DEL)
 
@@ -628,7 +663,7 @@
         iface_lan = self.lan
 
         a, b, c, _ = subnet_str.strip('/24').split('.')
-        bridge_ip = "%s.%s.%s.%s" % (a, b, c, BRIDGE_IP_LAST)
+        bridge_ip = f'{a}.{b}.{c}.{BRIDGE_IP_LAST}'
 
         configs = (iface_wlan, iface_lan, bridge_ip)
 
@@ -642,24 +677,21 @@
             send_ra_path: path where sendra path is located on server
         """
         self.scapy_install_path = self.ssh.run('mktemp -d').stdout.rstrip()
-        self.log.info("Scapy install path: %s" % self.scapy_install_path)
+        self.log.info(f'Scapy install path: {self.scapy_install_path}')
         self.ssh.send_file(scapy_path, self.scapy_install_path)
         self.ssh.send_file(send_ra_path, self.scapy_install_path)
 
         scapy = os.path.join(self.scapy_install_path,
                              scapy_path.split('/')[-1])
 
-        untar_res = self.ssh.run('tar -xvf %s -C %s' %
-                                 (scapy, self.scapy_install_path))
-
-        instl_res = self.ssh.run(
-            'cd %s; %s' % (self.scapy_install_path, SCAPY_INSTALL_COMMAND))
+        self.ssh.run(f'tar -xvf {scapy} -C {self.scapy_install_path}')
+        self.ssh.run(f'cd {self.scapy_install_path}; {SCAPY_INSTALL_COMMAND}')
 
     def cleanup_scapy(self):
         """ Cleanup scapy """
         if self.scapy_install_path:
-            cmd = 'rm -rf %s' % self.scapy_install_path
-            self.log.info("Cleaning up scapy %s" % cmd)
+            cmd = f'rm -rf {self.scapy_install_path}'
+            self.log.info(f'Cleaning up scapy {cmd}')
             output = self.ssh.run(cmd)
             self.scapy_install_path = None
 
@@ -681,10 +713,10 @@
           rtt: retrans timer of the RA packet
         """
         scapy_command = os.path.join(self.scapy_install_path, RA_SCRIPT)
-        options = ' -m %s -i %d -c %d -l %d -in %s -rtt %s' % (
-            mac, interval, count, lifetime, iface, rtt)
-        self.log.info("Scapy cmd: %s" % scapy_command + options)
-        res = self.ssh.run(scapy_command + options)
+        options = f' -m {mac} -i {interval} -c {count} -l {lifetime} -in {iface} -rtt {rtt}'
+        cmd = scapy_command + options
+        self.log.info(f'Scapy cmd: {cmd}')
+        self.ssh.run(cmd)
 
     def get_icmp6intype134(self):
         """Read the value of Icmp6InType134 and return integer.
@@ -692,8 +724,8 @@
         Returns:
             Integer value >0 if grep is successful; 0 otherwise.
         """
-        ra_count_str = self.ssh.run('grep Icmp6InType134 %s || true' %
-                                    PROC_NET_SNMP6).stdout
+        ra_count_str = self.ssh.run(
+            f'grep Icmp6InType134 {PROC_NET_SNMP6} || true').stdout
         if ra_count_str:
             return int(ra_count_str.split()[1])
 
@@ -762,13 +794,11 @@
         if hostapd_configs is None:
             hostapd_configs = []
 
-        self.log.info('Power cycling AccessPoint (%s)' %
-                      self.ssh_settings.hostname)
+        self.log.info(f'Power cycling')
         ap_pdu, ap_pdu_port = pdu.get_pdu_port_for_device(
             self.device_pdu_config, pdus)
 
-        self.log.info('Killing power to AccessPoint (%s)' %
-                      self.ssh_settings.hostname)
+        self.log.info(f'Killing power')
         ap_pdu.off(str(ap_pdu_port))
 
         self.log.info('Verifying AccessPoint is unreachable.')
@@ -783,12 +813,12 @@
                     'second.')
                 time.sleep(1)
         else:
-            raise ConnectionError('Failed to bring down AccessPoint (%s)' %
-                                  self.ssh_settings.hostname)
+            raise ConnectionError(
+                f'Failed to bring down AccessPoint ({self.ssh_settings.hostname})'
+            )
         self._aps.clear()
 
-        self.log.info('Restoring power to AccessPoint (%s)' %
-                      self.ssh_settings.hostname)
+        self.log.info(f'Restoring power')
         ap_pdu.on(str(ap_pdu_port))
 
         self.log.info('Waiting for AccessPoint to respond to pings.')
@@ -802,9 +832,9 @@
                                'Retrying in 1 second.')
                 time.sleep(1)
         else:
-            raise ConnectionError('Timed out waiting for AccessPoint (%s) to '
-                                  'respond to pings.' %
-                                  self.ssh_settings.hostname)
+            raise ConnectionError(
+                f'Timed out waiting for AccessPoint ({self.ssh_settings.hostname}) '
+                'to respond to pings.')
 
         self.log.info('Waiting for AccessPoint to allow ssh connection.')
         timeout = time.time() + ssh_timeout
@@ -819,15 +849,14 @@
                 self.log.info('AccessPoint available via ssh.')
                 break
         else:
-            raise ConnectionError('Timed out waiting for AccessPoint (%s) to '
-                                  'allow ssh connection.' %
-                                  self.ssh_settings.hostname)
+            raise ConnectionError(
+                f'Timed out waiting for AccessPoint ({self.ssh_settings.hostname}) '
+                'to allow ssh connection.')
 
         # Allow 5 seconds for OS to finish getting set up
         time.sleep(5)
         self._initial_ap()
-        self.log.info('AccessPoint (%s) power cycled successfully.' %
-                      self.ssh_settings.hostname)
+        self.log.info('Power cycled successfully')
 
         for settings in hostapd_configs:
             if type(settings) == hostapd_config.HostapdConfig:
@@ -845,8 +874,7 @@
                     'Items in hostapd_configs list must either be '
                     'hostapd.HostapdConfig objects or dictionaries.')
 
-            self.log.info('Restarting network (%s) on AccessPoint.' %
-                          config.ssid)
+            self.log.info(f'Restarting network {config.ssid}')
             self.start_ap(config,
                           setup_bridge=setup_bridge,
                           additional_parameters=additional_parameters)
@@ -854,14 +882,39 @@
     def channel_switch(self, identifier, channel_num):
         """Switch to a different channel on the given AP."""
         if identifier not in list(self._aps.keys()):
-            raise ValueError('Invalid identifier %s given' % identifier)
+            raise ValueError(f'Invalid identifier {identifier} given')
         instance = self._aps.get(identifier)
-        self.log.info('channel switch to channel {}'.format(channel_num))
+        self.log.info(f'channel switch to channel {channel_num}')
         instance.hostapd.channel_switch(channel_num)
 
     def get_current_channel(self, identifier):
         """Find the current channel on the given AP."""
         if identifier not in list(self._aps.keys()):
-            raise ValueError('Invalid identifier %s given' % identifier)
+            raise ValueError(f'Invalid identifier {identifier} given')
         instance = self._aps.get(identifier)
         return instance.hostapd.get_current_channel()
+
+    def get_stas(self, identifier) -> Set[str]:
+        """Return MAC addresses of all associated STAs on the given AP."""
+        if identifier not in list(self._aps.keys()):
+            raise ValueError(f'Invalid identifier {identifier} given')
+        instance = self._aps.get(identifier)
+        return instance.hostapd.get_stas()
+
+    def get_sta_extended_capabilities(self, identifier,
+                                      sta_mac: str) -> ExtendedCapabilities:
+        """Get extended capabilities for the given STA, as seen by the AP."""
+        if identifier not in list(self._aps.keys()):
+            raise ValueError(f'Invalid identifier {identifier} given')
+        instance = self._aps.get(identifier)
+        return instance.hostapd.get_sta_extended_capabilities(sta_mac)
+
+    def send_bss_transition_management_req(
+            self, identifier, sta_mac: str,
+            request: BssTransitionManagementRequest):
+        """Send a BSS Transition Management request to an associated STA."""
+        if identifier not in list(self._aps.keys()):
+            raise ValueError('Invalid identifier {identifier} given')
+        instance = self._aps.get(identifier)
+        return instance.hostapd.send_bss_transition_management_req(
+            sta_mac, request)
diff --git a/acts/framework/acts/controllers/android_device.py b/acts/framework/acts/controllers/android_device.py
index ca334aa..4e66b42 100755
--- a/acts/framework/acts/controllers/android_device.py
+++ b/acts/framework/acts/controllers/android_device.py
@@ -559,6 +559,17 @@
         }
         return info
 
+    def add_device_info(self, name, info):
+        """Add custom device info to the user_added_info section.
+
+        Adding the same info name the second time will override existing info.
+
+        Args:
+          name: string, name of this info.
+          info: serializable, content of the info.
+        """
+        self._user_added_device_info.update({name: info})
+
     def sdk_api_level(self):
         if self._sdk_api_level is not None:
             return self._sdk_api_level
@@ -971,11 +982,11 @@
         Returns:
         Linux UID for the apk.
         """
-        output = self.adb.shell("dumpsys package %s | grep userId=" % apk_name,
+        output = self.adb.shell("dumpsys package %s | grep -e userId= -e appId=" % apk_name,
                                 ignore_status=True)
-        result = re.search(r"userId=(\d+)", output)
+        result = re.search(r"userId=(\d+)|appId=(\d+)", output)
         if result:
-            return result.group(1)
+            return result.group(1) if result.group(1) else result.group(2)
         else:
             None
 
@@ -1387,7 +1398,7 @@
             try:
                 completed = self.adb.getprop("sys.boot_completed")
                 if completed == '1':
-                    self.log.debug("devie has rebooted")
+                    self.log.debug("Device has rebooted")
                     return
             except AdbError:
                 # adb shell calls may fail during certain period of booting
diff --git a/acts/framework/acts/controllers/android_lib/android_api.py b/acts/framework/acts/controllers/android_lib/android_api.py
index 634e44b..b2b8abd 100644
--- a/acts/framework/acts/controllers/android_lib/android_api.py
+++ b/acts/framework/acts/controllers/android_lib/android_api.py
@@ -13,7 +13,6 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-import enum
 import logging
 import sys
 
@@ -36,8 +35,7 @@
     MAX = sys.maxsize
 
 
-def android_api(min_api=AndroidApi.OLDEST,
-                max_api=AndroidApi.LATEST):
+def android_api(min_api=AndroidApi.OLDEST, max_api=AndroidApi.LATEST):
     """Decorates a function to only be called for the given API range.
 
     Only gets called if the AndroidDevice in the args is within the specified
@@ -55,12 +53,14 @@
          min_api: The minimum API level. Can be an int or an AndroidApi value.
          max_api: The maximum API level. Can be an int or an AndroidApi value.
     """
+
     def get_api_level(*args, **_):
         for arg in args:
             if isinstance(arg, AndroidDevice):
                 return arg.sdk_api_level()
-        logging.getLogger().error('An AndroidDevice was not found in the given '
-                                  'arguments.')
+        logging.getLogger().error(
+            'An AndroidDevice was not found in the given '
+            'arguments.')
         return None
 
     return version_selector.set_version(get_api_level, min_api, max_api)
diff --git a/acts/framework/acts/controllers/anritsu_lib/md8475_cellular_simulator.py b/acts/framework/acts/controllers/anritsu_lib/md8475_cellular_simulator.py
index a1b15db..79b35a6 100644
--- a/acts/framework/acts/controllers/anritsu_lib/md8475_cellular_simulator.py
+++ b/acts/framework/acts/controllers/anritsu_lib/md8475_cellular_simulator.py
@@ -93,7 +93,7 @@
             self.anritsu.get_BTS(md8475a.BtsNumber.BTS2)
         ]
 
-    def set_band_combination(self, bands):
+    def set_band_combination(self, bands, mimo_modes):
         """ Prepares the test equipment for the indicated band combination.
 
         The reason why this is implemented in a separate method and not calling
@@ -104,6 +104,7 @@
 
         Args:
             bands: a list of bands represented as ints or strings
+            mimo_modes: a list of LteSimulation.MimoMode to use for each carrier
         """
         self.num_carriers = len(bands)
 
@@ -706,7 +707,7 @@
     LTE_MAX_CARRIERS = 4
 
     # The maximum power that the equipment is able to transmit
-    MAX_DL_POWER = -30
+    MAX_DL_POWER = -10
 
     # Simulation config files in the callbox computer.
     # These should be replaced in the future by setting up
diff --git a/acts/framework/acts/controllers/anritsu_lib/mg3710a.py b/acts/framework/acts/controllers/anritsu_lib/mg3710a.py
index a9df65d..14b0d90 100644
--- a/acts/framework/acts/controllers/anritsu_lib/mg3710a.py
+++ b/acts/framework/acts/controllers/anritsu_lib/mg3710a.py
@@ -18,10 +18,7 @@
 """
 
 import logging
-import time
 import socket
-from enum import Enum
-from enum import IntEnum
 
 from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
 from acts.controllers.anritsu_lib._anritsu_utils import NO_ERROR
@@ -56,8 +53,8 @@
         self.log.info("Opening Socket Connection with "
                       "Signal Generator MG3710A ({}) ".format(self._ipaddr))
         try:
-            self._sock = socket.create_connection(
-                (self._ipaddr, 49158), timeout=30)
+            self._sock = socket.create_connection((self._ipaddr, 49158),
+                                                  timeout=30)
             self.send_query("*IDN?", 60)
             self.log.info("Communication Signal Generator MG3710A OK.")
             self.log.info("Opened Socket connection to ({})"
@@ -698,8 +695,8 @@
         Returns:
             frequency offset
         """
-        return self.send_query(
-            "SOUR{}:RAD:ARB:WM{}:FREQ:OFFS?".format(sg, a_or_b))
+        return self.send_query("SOUR{}:RAD:ARB:WM{}:FREQ:OFFS?".format(
+            sg, a_or_b))
 
     def set_arb_freq_offset_aorb(self, a_or_b, offset, sg=1):
         """ Sets the frequency offset of Pattern A/Pattern B based on Baseband
diff --git a/acts/framework/acts/controllers/ap_lib/ap_iwconfig.py b/acts/framework/acts/controllers/ap_lib/ap_iwconfig.py
index 77b9263..16397e2 100644
--- a/acts/framework/acts/controllers/ap_lib/ap_iwconfig.py
+++ b/acts/framework/acts/controllers/ap_lib/ap_iwconfig.py
@@ -14,7 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import logging
 from acts.libs.proc import job
 
 
diff --git a/acts/framework/acts/controllers/ap_lib/dhcp_config.py b/acts/framework/acts/controllers/ap_lib/dhcp_config.py
index ffc9db1..d7b3d02 100644
--- a/acts/framework/acts/controllers/ap_lib/dhcp_config.py
+++ b/acts/framework/acts/controllers/ap_lib/dhcp_config.py
@@ -12,9 +12,7 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import collections
 import copy
-import ipaddress
 
 _ROUTER_DNS = '8.8.8.8, 4.4.4.4'
 
diff --git a/acts/framework/acts/controllers/ap_lib/extended_capabilities.py b/acts/framework/acts/controllers/ap_lib/extended_capabilities.py
new file mode 100644
index 0000000..39d2f36
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/extended_capabilities.py
@@ -0,0 +1,193 @@
+#!/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.
+
+from enum import IntEnum, unique
+from typing import Tuple
+
+
+@unique
+class ExtendedCapability(IntEnum):
+    """All extended capabilities present in IEEE 802.11-2020 Table 9-153.
+
+    Each name has a value corresponding to that extended capability's bit offset
+    in the specification's extended capabilities field.
+
+    Note that most extended capabilities are represented by a single bit, which
+    indicates whether the extended capability is advertised by the STA; but
+    some are represented by multiple bits. In the enum, each extended capability
+    has the value of its offset; comments indicate capabilities that use
+    multiple bits.
+    """
+    TWENTY_FORTY_BSS_COEXISTENCE_MANAGEMENT_SUPPORT = 0
+    GLK = 1
+    EXTENDED_CHANNEL_SWITCHING = 2
+    GLK_GCR = 3
+    PSMP_CAPABILITY = 4
+    # 5 reserved
+    S_PSMP_SUPPORT = 6
+    EVENT = 7
+    DIAGNOSTICS = 8
+    MULTICAST_DIAGNOSTICS = 9
+    LOCATION_TRACKING = 10
+    FMS = 11
+    PROXY_ARP_SERVICE = 12
+    COLLOCATED_INTERFERENCE_REPORTING = 13
+    CIVIC_LOCATION = 14
+    GEOSPATIAL_LOCATION = 15
+    TFS = 16
+    WNM_SLEEP_MODE = 17
+    TIM_BROADCAST = 18
+    BSS_TRANSITION = 19
+    QOS_TRAFFIC_CAPABILITY = 20
+    AC_STATION_COUNT = 21
+    MULTIPLE_BSSID = 22
+    TIMING_MEASUREMENT = 23
+    CHANNEL_USAGE = 24
+    SSID_LIST = 25
+    DMS = 26
+    UTC_TSF_OFFSET = 27
+    TPU_BUFFER_STA_SUPPORT = 28
+    TDLS_PEER_PSM_SUPPORT = 29
+    TDLS_CHANNEL_SWITCHING = 30
+    INTERWORKING = 31
+    QOS_MAP = 32
+    EBR = 33
+    SSPN_INTERFACE = 34
+    # 35 reserved
+    MSGCF_CAPABILITY = 36
+    TDLS_SUPPORT = 37
+    TDLS_PROHIBITED = 38
+    TDLS_CHANNEL_SWITCHING_PROHIBITED = 39
+    REJECT_UNADMITTED_FRAME = 40
+    SERVICE_INTERVAL_GRANULARITY = 41
+    # Bits 41-43 contain SERVICE_INTERVAL_GRANULARITY value
+    IDENTIFIER_LOCATION = 44
+    U_APSD_COEXISTENCE = 45
+    WNM_NOTIFICATION = 46
+    QAB_CAPABILITY = 47
+    UTF_8_SSID = 48
+    QMF_ACTIVATED = 49
+    QMF_RECONFIGURATION_ACTIVATED = 50
+    ROBUST_AV_STREAMING = 51
+    ADVANCED_GCR = 52
+    MESH_GCR = 53
+    SCS = 54
+    QLOAD_REPORT = 55
+    ALTERNATE_EDCA = 56
+    UNPROTECTED_TXOP_NEGOTIATION = 57
+    PROTECTED_TXOP_NEGOTIATION = 58
+    # 59 reserved
+    PROTECTED_QLOAD_REPORT = 60
+    TDLS_WIDER_BANDWIDTH = 61
+    OPERATING_MODE_NOTIFICATION = 62
+    MAX_NUMBER_OF_MSDUS_IN_A_MSDU = 63
+    # 63-64 contain MAX_NUMBER_OF_MSDUS_IN_A_MSDU value
+    CHANNEL_SCHEDULE_MANAGEMENT = 65
+    GEODATABASE_INBAND_ENABLING_SIGNAL = 66
+    NETWORK_CHANNEL_CONTROL = 67
+    WHITE_SPACE_MAP = 68
+    CHANNEL_AVAILABILITY_QUERY = 69
+    FINE_TIMING_MEASUREMENT_RESPONDER = 70
+    FINE_TIMING_MEASUREMENT_INITIATOR = 71
+    FILS_CAPABILITY = 72
+    EXTENDED_SPECTRUM_MANAGEMENT_CAPABLE = 73
+    FUTURE_CHANNEL_GUIDANCE = 74
+    PAD = 75
+    # 76-79 reserved
+    COMPLETE_LIST_OF_NON_TX_BSSID_PROFILES = 80
+    SAE_PASSWORD_IDENTIFIERS_IN_USE = 81
+    SAE_PASSWORD_IDENTIFIERS_USED_EXCLUSIVELY = 82
+    # 83 reserved
+    BEACON_PROTECTION_ENABLED = 84
+    MIRRORED_SCS = 85
+    # 86 reserved
+    LOCAL_MAC_ADDRESS_POLICY = 87
+    # 88-n reserved
+
+
+def _offsets(ext_cap_offset: ExtendedCapability) -> Tuple[int, int]:
+    """For given capability, return the byte and bit offsets within the field.
+
+    802.11 divides the extended capability field into bytes, as does the
+    ExtendedCapabilities class below. This function returns the index of the
+    byte that contains the given extended capability, as well as the bit offset
+    inside that byte (all offsets zero-indexed). For example,
+    MULTICAST_DIAGNOSTICS is bit 9, which is within byte 1 at bit offset 1.
+    """
+    byte_offset = ext_cap_offset // 8
+    bit_offset = ext_cap_offset % 8
+    return byte_offset, bit_offset
+
+
+class ExtendedCapabilities:
+    """Extended capability parsing and representation.
+
+    See IEEE 802.11-2020 9.4.2.26.
+    """
+
+    def __init__(self, ext_cap: bytearray = bytearray()):
+        """Represent the given extended capabilities field.
+
+        Args:
+            ext_cap: IEEE 802.11-2020 9.4.2.26 extended capabilities field.
+            Default is an empty field, meaning no extended capabilities are
+            advertised.
+        """
+        self._ext_cap = ext_cap
+
+    def _capability_advertised(self, ext_cap: ExtendedCapability) -> bool:
+        """Whether an extended capability is advertised.
+
+        Args:
+            ext_cap: an extended capability.
+        Returns:
+            True if the bit is present and its value is 1, otherwise False.
+        Raises:
+            NotImplementedError: for extended capabilities that span more than
+            a single bit. These could be supported, but no callers need them
+            at this time.
+        """
+        if ext_cap in [
+                ExtendedCapability.SERVICE_INTERVAL_GRANULARITY,
+                ExtendedCapability.MAX_NUMBER_OF_MSDUS_IN_A_MSDU
+        ]:
+            raise NotImplementedError(
+                f'{ext_cap.name} not implemented yet by {__class__}')
+        byte_offset, bit_offset = _offsets(ext_cap)
+        if len(self._ext_cap) > byte_offset:
+            # Use bit_offset to derive a mask that will check the correct bit.
+            if self._ext_cap[byte_offset] & 2**bit_offset > 0:
+                return True
+        return False
+
+    @property
+    def bss_transition(self) -> bool:
+        return self._capability_advertised(ExtendedCapability.BSS_TRANSITION)
+
+    @property
+    def proxy_arp_service(self) -> bool:
+        return self._capability_advertised(
+            ExtendedCapability.PROXY_ARP_SERVICE)
+
+    @property
+    def utc_tsf_offset(self) -> bool:
+        return self._capability_advertised(ExtendedCapability.UTC_TSF_OFFSET)
+
+    @property
+    def wnm_sleep_mode(self) -> bool:
+        return self._capability_advertised(ExtendedCapability.WNM_SLEEP_MODE)
+
+    # Other extended capability property methods can be added as needed by callers.
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd.py b/acts/framework/acts/controllers/ap_lib/hostapd.py
index d08caf2..c2cfc54 100644
--- a/acts/framework/acts/controllers/ap_lib/hostapd.py
+++ b/acts/framework/acts/controllers/ap_lib/hostapd.py
@@ -15,13 +15,16 @@
 import collections
 import itertools
 import logging
-import os
 import re
 import time
+from typing import Set
 
 from acts.controllers.ap_lib import hostapd_config
 from acts.controllers.ap_lib import hostapd_constants
+from acts.controllers.ap_lib.extended_capabilities import ExtendedCapabilities
+from acts.controllers.ap_lib.wireless_network_management import BssTransitionManagementRequest
 from acts.controllers.utils_lib.commands import shell
+from acts.libs.proc.job import Result
 
 
 class Error(Exception):
@@ -137,6 +140,116 @@
             raise Error('Internal error: current channel could not be parsed')
         return channel
 
+    def _list_sta(self) -> Result:
+        """List all associated STA MAC addresses.
+
+        Returns:
+            acts.libs.proc.job.Result containing the results of the command.
+        Raises: See _run_hostapd_cli_cmd
+        """
+        list_sta_cmd = 'list_sta'
+        return self._run_hostapd_cli_cmd(list_sta_cmd)
+
+    def get_stas(self) -> Set[str]:
+        """Return MAC addresses of all associated STAs."""
+        list_sta_result = self._list_sta()
+        stas = set()
+        for line in list_sta_result.stdout.splitlines():
+            # Each line must be a valid MAC address. Capture it.
+            m = re.match(r'((?:[0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2})', line)
+            if m:
+                stas.add(m.group(1))
+        return stas
+
+    def _sta(self, sta_mac: str) -> Result:
+        """Return hostapd's detailed info about an associated STA.
+
+        Returns:
+            acts.libs.proc.job.Result containing the results of the command.
+        Raises: See _run_hostapd_cli_cmd
+        """
+        sta_cmd = 'sta {}'.format(sta_mac)
+        return self._run_hostapd_cli_cmd(sta_cmd)
+
+    def get_sta_extended_capabilities(self,
+                                      sta_mac: str) -> ExtendedCapabilities:
+        """Get extended capabilities for the given STA, as seen by the AP.
+
+        Args:
+            sta_mac: MAC address of the STA in question.
+        Returns:
+            Extended capabilities of the given STA.
+        Raises:
+            Error if extended capabilities for the STA cannot be obtained.
+        """
+        sta_result = self._sta(sta_mac)
+        # hostapd ext_capab field is a hex encoded string representation of the
+        # 802.11 extended capabilities structure, each byte represented by two
+        # chars (each byte having format %02x).
+        m = re.search(r'ext_capab=([0-9A-Faf]+)', sta_result.stdout,
+                      re.MULTILINE)
+        if not m:
+            raise Error('Failed to get ext_capab from STA details')
+        raw_ext_capab = m.group(1)
+        try:
+            return ExtendedCapabilities(bytearray.fromhex(raw_ext_capab))
+        except ValueError:
+            raise Error(
+                f'ext_capab contains invalid hex string repr {raw_ext_capab}')
+
+    def _bss_tm_req(self, client_mac: str,
+                    request: BssTransitionManagementRequest) -> Result:
+        """Send a hostapd BSS Transition Management request command to a STA.
+
+        Args:
+            client_mac: MAC address that will receive the request.
+            request: BSS Transition Management request that will be sent.
+        Returns:
+            acts.libs.proc.job.Result containing the results of the command.
+        Raises: See _run_hostapd_cli_cmd
+        """
+        bss_tm_req_cmd = f'bss_tm_req {client_mac}'
+
+        if request.abridged:
+            bss_tm_req_cmd += ' abridged=1'
+        if request.bss_termination_included and request.bss_termination_duration:
+            bss_tm_req_cmd += f' bss_term={request.bss_termination_duration.duration}'
+        if request.disassociation_imminent:
+            bss_tm_req_cmd += ' disassoc_imminent=1'
+        if request.disassociation_timer is not None:
+            bss_tm_req_cmd += f' disassoc_timer={request.disassociation_timer}'
+        if request.preferred_candidate_list_included:
+            bss_tm_req_cmd += ' pref=1'
+        if request.session_information_url:
+            bss_tm_req_cmd += f' url={request.session_information_url}'
+        if request.validity_interval:
+            bss_tm_req_cmd += f' valid_int={request.validity_interval}'
+
+        # neighbor= can appear multiple times, so it requires special handling.
+        for neighbor in request.candidate_list:
+            bssid = neighbor.bssid
+            bssid_info = hex(neighbor.bssid_information)
+            op_class = neighbor.operating_class
+            chan_num = neighbor.channel_number
+            phy_type = int(neighbor.phy_type)
+            bss_tm_req_cmd += f' neighbor={bssid},{bssid_info},{op_class},{chan_num},{phy_type}'
+
+        return self._run_hostapd_cli_cmd(bss_tm_req_cmd)
+
+    def send_bss_transition_management_req(
+            self, sta_mac: str,
+            request: BssTransitionManagementRequest) -> Result:
+        """Send a BSS Transition Management request to an associated STA.
+
+        Args:
+            sta_mac: MAC address of the STA in question.
+            request: BSS Transition Management request that will be sent.
+        Returns:
+            acts.libs.proc.job.Result containing the results of the command.
+        Raises: See _run_hostapd_cli_cmd
+        """
+        return self._bss_tm_req(sta_mac, request)
+
     def is_alive(self):
         """
         Returns:
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd_ap_preset.py b/acts/framework/acts/controllers/ap_lib/hostapd_ap_preset.py
index e31fb03..62562c4 100644
--- a/acts/framework/acts/controllers/ap_lib/hostapd_ap_preset.py
+++ b/acts/framework/acts/controllers/ap_lib/hostapd_ap_preset.py
@@ -12,6 +12,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+from typing import FrozenSet
+
 from acts import utils
 
 import acts.controllers.ap_lib.third_party_ap_profiles.actiontec as actiontec
@@ -40,26 +42,28 @@
     return var if var is not None else default_value
 
 
-def create_ap_preset(profile_name='whirlwind',
-                     iface_wlan_2g=None,
-                     iface_wlan_5g=None,
-                     channel=None,
-                     mode=None,
-                     frequency=None,
-                     security=None,
-                     pmf_support=None,
-                     ssid=None,
-                     hidden=None,
-                     dtim_period=None,
-                     frag_threshold=None,
-                     rts_threshold=None,
-                     force_wmm=None,
-                     beacon_interval=None,
-                     short_preamble=None,
-                     n_capabilities=None,
-                     ac_capabilities=None,
-                     vht_bandwidth=None,
-                     bss_settings=[]):
+def create_ap_preset(
+        profile_name='whirlwind',
+        iface_wlan_2g=None,
+        iface_wlan_5g=None,
+        channel=None,
+        mode=None,
+        frequency=None,
+        security=None,
+        pmf_support=None,
+        ssid=None,
+        hidden=None,
+        dtim_period=None,
+        frag_threshold=None,
+        rts_threshold=None,
+        force_wmm=None,
+        beacon_interval=None,
+        short_preamble=None,
+        n_capabilities=None,
+        ac_capabilities=None,
+        vht_bandwidth=None,
+        wnm_features: FrozenSet[hostapd_constants.WnmFeature] = frozenset(),
+        bss_settings=[]):
     """AP preset config generator.  This a wrapper for hostapd_config but
        but supplies the default settings for the preset that is selected.
 
@@ -89,6 +93,7 @@
             rts/cts or cts to self.
         n_capabilities: 802.11n capabilities for for BSS to advertise.
         ac_capabilities: 802.11ac capabilities for for BSS to advertise.
+        wnm_features: WNM features to enable on the AP.
 
     Returns: A hostapd_config object that can be used by the hostapd object.
     """
@@ -141,6 +146,7 @@
                 n_capabilities=n_capabilities,
                 frag_threshold=frag_threshold,
                 rts_threshold=rts_threshold,
+                wnm_features=wnm_features,
                 bss_settings=bss_settings)
         else:
             interface = iface_wlan_5g
@@ -224,7 +230,8 @@
                                   frag_threshold=frag_threshold,
                                   n_capabilities=[],
                                   ac_capabilities=[],
-                                  vht_bandwidth=None)
+                                  vht_bandwidth=None,
+                                  wnm_features=wnm_features)
     elif profile_name == 'whirlwind_11ag_legacy':
         if frequency < 5000:
             mode = hostapd_constants.MODE_11G
@@ -247,7 +254,8 @@
                                   frag_threshold=frag_threshold,
                                   n_capabilities=[],
                                   ac_capabilities=[],
-                                  vht_bandwidth=None)
+                                  vht_bandwidth=None,
+                                  wnm_features=wnm_features)
     elif profile_name == 'mistral':
         hidden = _get_or_default(hidden, False)
         force_wmm = _get_or_default(force_wmm, True)
@@ -296,6 +304,7 @@
                 n_capabilities=n_capabilities,
                 frag_threshold=frag_threshold,
                 rts_threshold=rts_threshold,
+                wnm_features=wnm_features,
                 bss_settings=bss_settings,
                 additional_parameters=additional_params,
                 set_ap_defaults_profile=profile_name)
@@ -364,6 +373,7 @@
                 rts_threshold=rts_threshold,
                 n_capabilities=n_capabilities,
                 ac_capabilities=ac_capabilities,
+                wnm_features=wnm_features,
                 bss_settings=bss_settings,
                 additional_parameters=additional_params,
                 set_ap_defaults_profile=profile_name)
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd_config.py b/acts/framework/acts/controllers/ap_lib/hostapd_config.py
index be990f7..ce508f5 100644
--- a/acts/framework/acts/controllers/ap_lib/hostapd_config.py
+++ b/acts/framework/acts/controllers/ap_lib/hostapd_config.py
@@ -12,11 +12,9 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import enum
-import logging
-import os
 import collections
-import itertools
+import logging
+from typing import FrozenSet
 
 from acts.controllers.ap_lib import hostapd_constants
 
@@ -71,6 +69,7 @@
 
     All the settings for a router that are not part of an ssid.
     """
+
     def _get_11ac_center_channel_from_channel(self, channel):
         """Returns the center channel of the selected channel band based
            on the channel and channel bandwidth provided.
@@ -132,11 +131,7 @@
     @property
     def _require_ht(self):
         """Returns: True iff clients should be required to support HT."""
-        # TODO(wiley) Why? (crbug.com/237370)
-        # DOES THIS APPLY TO US?
-        logging.warning('Not enforcing pure N mode because Snow does '
-                        'not seem to support it...')
-        return False
+        return self._mode == hostapd_constants.MODE_11N_PURE
 
     @property
     def _require_vht(self):
@@ -308,6 +303,14 @@
         """Returns: int, _min_streams value, or None."""
         return self._min_streams
 
+    @property
+    def wnm_features(self) -> FrozenSet[hostapd_constants.WnmFeature]:
+        return self._wnm_features
+
+    @wnm_features.setter
+    def wnm_features(self, value: FrozenSet[hostapd_constants.WnmFeature]):
+        self._wnm_features = value
+
     def __init__(self,
                  interface=None,
                  mode=None,
@@ -333,6 +336,8 @@
                  spectrum_mgmt_required=None,
                  scenario_name=None,
                  min_streams=None,
+                 wnm_features: FrozenSet[
+                     hostapd_constants.WnmFeature] = frozenset(),
                  bss_settings=[],
                  additional_parameters={},
                  set_ap_defaults_profile='whirlwind'):
@@ -375,6 +380,7 @@
             scenario_name: string to be included in file names, instead
                 of the interface name.
             min_streams: int, number of spatial streams required.
+            wnm_features: WNM features to enable on the AP.
             control_interface: The file name to use as the control interface.
             bss_settings: The settings for all bss.
             additional_parameters: A dictionary of additional parameters to add
@@ -445,12 +451,12 @@
                 self._wmm_enabled = 0
         # Default PMF Values
         if pmf_support is None:
-            if (self.security and self.security.security_mode_string
-                    == hostapd_constants.WPA3_STRING):
+            if (self.security and self.security.security_mode_string ==
+                    hostapd_constants.WPA3_STRING):
                 # Set PMF required for WP3
                 self._pmf_support = hostapd_constants.PMF_SUPPORT_REQUIRED
-            elif (self.security and self.security.security_mode_string
-                  in hostapd_constants.WPA3_MODE_STRINGS):
+            elif (self.security and self.security.security_mode_string in
+                  hostapd_constants.WPA3_MODE_STRINGS):
                 # Default PMF to enabled for WPA3 mixed modes (can be
                 # overwritten by explicitly provided value)
                 self._pmf_support = hostapd_constants.PMF_SUPPORT_ENABLED
@@ -461,8 +467,8 @@
         elif pmf_support not in hostapd_constants.PMF_SUPPORT_VALUES:
             raise ValueError('Invalid value for pmf_support: %r' % pmf_support)
         elif (pmf_support != hostapd_constants.PMF_SUPPORT_REQUIRED
-              and self.security and self.security.security_mode_string
-              == hostapd_constants.WPA3_STRING):
+              and self.security and self.security.security_mode_string ==
+              hostapd_constants.WPA3_STRING):
             raise ValueError('PMF support must be required with wpa3.')
         else:
             self._pmf_support = pmf_support
@@ -495,6 +501,7 @@
         self._spectrum_mgmt_required = spectrum_mgmt_required
         self._scenario_name = scenario_name
         self._min_streams = min_streams
+        self._wnm_features = wnm_features
         self._additional_parameters = additional_parameters
 
         self._bss_lookup = collections.OrderedDict()
@@ -646,6 +653,22 @@
                 bss_conf[k] = v
             all_conf.append(bss_conf)
 
+        for wnm_feature in self._wnm_features:
+            if wnm_feature == hostapd_constants.WnmFeature.TIME_ADVERTISEMENT:
+                conf.update(hostapd_constants.ENABLE_WNM_TIME_ADVERTISEMENT)
+            elif wnm_feature == hostapd_constants.WnmFeature.WNM_SLEEP_MODE:
+                conf.update(hostapd_constants.ENABLE_WNM_SLEEP_MODE)
+            elif wnm_feature == hostapd_constants.WnmFeature.BSS_TRANSITION_MANAGEMENT:
+                conf.update(
+                    hostapd_constants.ENABLE_WNM_BSS_TRANSITION_MANAGEMENT)
+            elif wnm_feature == hostapd_constants.WnmFeature.PROXY_ARP:
+                conf.update(hostapd_constants.ENABLE_WNM_PROXY_ARP)
+            elif wnm_feature == hostapd_constants.WnmFeature.IPV6_NEIGHBOR_ADVERTISEMENT_MULTICAST_TO_UNICAST:
+                conf.update(
+                    hostapd_constants.
+                    ENABLE_WNM_IPV6_NEIGHBOR_ADVERTISEMENT_MULTICAST_TO_UNICAST
+                )
+
         if self._additional_parameters:
             all_conf.append(self._additional_parameters)
 
diff --git a/acts/framework/acts/controllers/ap_lib/hostapd_constants.py b/acts/framework/acts/controllers/ap_lib/hostapd_constants.py
index 7480257..ad3cfe8 100755
--- a/acts/framework/acts/controllers/ap_lib/hostapd_constants.py
+++ b/acts/framework/acts/controllers/ap_lib/hostapd_constants.py
@@ -16,6 +16,8 @@
 
 import itertools
 
+from enum import Enum, auto, unique
+
 BAND_2G = '2g'
 BAND_5G = '5g'
 CHANNEL_BANDWIDTH_20MHZ = 20
@@ -535,6 +537,15 @@
 ENABLE_RRM_BEACON_REPORT = {'rrm_beacon_report': 1}
 ENABLE_RRM_NEIGHBOR_REPORT = {'rrm_neighbor_report': 1}
 
+# Wireless Network Management (AKA 802.11v) features.
+ENABLE_WNM_TIME_ADVERTISEMENT = {'time_advertisement': 2, 'time_zone': 'EST5'}
+ENABLE_WNM_SLEEP_MODE = {'wnm_sleep_mode': 1}
+ENABLE_WNM_BSS_TRANSITION_MANAGEMENT = {'bss_transition': 1}
+ENABLE_WNM_PROXY_ARP = {'proxy_arp': 1}
+ENABLE_WNM_IPV6_NEIGHBOR_ADVERTISEMENT_MULTICAST_TO_UNICAST = {
+    'na_mcast_to_ucast': 1
+}
+
 VENDOR_IE = {
     'correct_length_beacon': {
         'vendor_elements': 'dd0411223301'
@@ -1387,3 +1398,13 @@
 }
 
 ALL_CHANNELS = {**ALL_CHANNELS_2G, **ALL_CHANNELS_5G}
+
+
+@unique
+class WnmFeature(Enum):
+    """Wireless Network Management (AKA 802.11v) features hostapd supports."""
+    TIME_ADVERTISEMENT = auto()
+    WNM_SLEEP_MODE = auto()
+    BSS_TRANSITION_MANAGEMENT = auto()
+    PROXY_ARP = auto()
+    IPV6_NEIGHBOR_ADVERTISEMENT_MULTICAST_TO_UNICAST = auto()
diff --git a/acts/framework/acts/controllers/ap_lib/radio_measurement.py b/acts/framework/acts/controllers/ap_lib/radio_measurement.py
new file mode 100644
index 0000000..dc009e9
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/radio_measurement.py
@@ -0,0 +1,231 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from enum import IntEnum, unique
+
+
+@unique
+class ApReachability(IntEnum):
+    """Neighbor Report AP Reachability values.
+
+    See IEEE 802.11-2020 Figure 9-172.
+    """
+    NOT_REACHABLE = 1
+    UNKNOWN = 2
+    REACHABLE = 3
+
+
+class BssidInformationCapabilities:
+    """Representation of Neighbor Report BSSID Information Capabilities.
+
+    See IEEE 802.11-2020 Figure 9-338 and 9.4.1.4.
+    """
+
+    def __init__(self,
+                 spectrum_management: bool = False,
+                 qos: bool = False,
+                 apsd: bool = False,
+                 radio_measurement: bool = False):
+        """Create a capabilities object.
+
+        Args:
+            spectrum_management: whether spectrum management is required.
+            qos: whether QoS is implemented.
+            apsd: whether APSD is implemented.
+            radio_measurement: whether radio measurement is activated.
+        """
+        self._spectrum_management = spectrum_management
+        self._qos = qos
+        self._apsd = apsd
+        self._radio_measurement = radio_measurement
+
+    def __index__(self) -> int:
+        """Convert to numeric representation of the field's bits."""
+        return self.spectrum_management << 5 \
+            | self.qos << 4 \
+            | self.apsd << 3 \
+            | self.radio_measurement << 2
+
+    @property
+    def spectrum_management(self) -> bool:
+        return self._spectrum_management
+
+    @property
+    def qos(self) -> bool:
+        return self._qos
+
+    @property
+    def apsd(self) -> bool:
+        return self._apsd
+
+    @property
+    def radio_measurement(self) -> bool:
+        return self._radio_measurement
+
+
+class BssidInformation:
+    """Representation of Neighbor Report BSSID Information field.
+
+    BssidInformation contains info about a neighboring AP, to be included in a
+    neighbor report element. See IEEE 802.11-2020 Figure 9-337.
+    """
+
+    def __init__(self,
+                 ap_reachability: ApReachability = ApReachability.UNKNOWN,
+                 security: bool = False,
+                 key_scope: bool = False,
+                 capabilities:
+                 BssidInformationCapabilities = BssidInformationCapabilities(),
+                 mobility_domain: bool = False,
+                 high_throughput: bool = False,
+                 very_high_throughput: bool = False,
+                 ftm: bool = False):
+        """Create a BSSID Information object for a neighboring AP.
+
+        Args:
+            ap_reachability: whether this AP is reachable by the STA that
+                requested the neighbor report.
+            security: whether this AP is known to support the same security
+                provisioning as used by the STA in its current association.
+            key_scope: whether this AP is known to have the same
+                authenticator as the AP sending the report.
+            capabilities: selected capabilities of this AP.
+            mobility_domain: whether the AP is including an MDE in its beacon
+                frames and the contents of that MDE are identical to the MDE
+                advertised by the AP sending the report.
+            high_throughput: whether the AP is an HT AP including the HT
+                Capabilities element in its Beacons, and that the contents of
+                that HT capabilities element are identical to the HT
+                capabilities element advertised by the AP sending the report.
+            very_high_throughput: whether the AP is a VHT AP and the VHT
+                capabilities element, if included as a subelement, is
+                identical in content to the VHT capabilities element included
+                in the AP’s beacon.
+            ftm: whether the AP is known to have the Fine Timing Measurement
+                Responder extended capability.
+        """
+        self._ap_reachability = ap_reachability
+        self._security = security
+        self._key_scope = key_scope
+        self._capabilities = capabilities
+        self._mobility_domain = mobility_domain
+        self._high_throughput = high_throughput
+        self._very_high_throughput = very_high_throughput
+        self._ftm = ftm
+
+    def __index__(self) -> int:
+        """Convert to numeric representation of the field's bits."""
+        return self._ap_reachability << 30 \
+            | self.security << 29 \
+            | self.key_scope << 28 \
+            | int(self.capabilities) << 22 \
+            | self.mobility_domain << 21 \
+            | self.high_throughput << 20 \
+            | self.very_high_throughput << 19 \
+            | self.ftm << 18
+
+    @property
+    def security(self) -> bool:
+        return self._security
+
+    @property
+    def key_scope(self) -> bool:
+        return self._key_scope
+
+    @property
+    def capabilities(self) -> BssidInformationCapabilities:
+        return self._capabilities
+
+    @property
+    def mobility_domain(self) -> bool:
+        return self._mobility_domain
+
+    @property
+    def high_throughput(self) -> bool:
+        return self._high_throughput
+
+    @property
+    def very_high_throughput(self) -> bool:
+        return self._very_high_throughput
+
+    @property
+    def ftm(self) -> bool:
+        return self._ftm
+
+
+@unique
+class PhyType(IntEnum):
+    """PHY type values, see dot11PhyType in 802.11-2020 Annex C."""
+    DSSS = 2
+    OFDM = 4
+    HRDSS = 5
+    ERP = 6
+    HT = 7
+    DMG = 8
+    VHT = 9
+    TVHT = 10
+    S1G = 11
+    CDMG = 12
+    CMMG = 13
+
+
+class NeighborReportElement:
+    """Representation of Neighbor Report element.
+
+    See IEEE 802.11-2020 9.4.2.36.
+    """
+
+    def __init__(self, bssid: str, bssid_information: BssidInformation,
+                 operating_class: int, channel_number: int, phy_type: PhyType):
+        """Create a neighbor report element.
+
+        Args:
+            bssid: MAC address of the neighbor.
+            bssid_information: BSSID Information of the neigbor.
+            operating_class: operating class of the neighbor.
+            channel_number: channel number of the neighbor.
+            phy_type: dot11PhyType of the neighbor.
+        """
+        self._bssid = bssid
+        self._bssid_information = bssid_information
+
+        # Operating Class, IEEE 802.11-2020 Annex E.
+        self._operating_class = operating_class
+
+        self._channel_number = channel_number
+
+        # PHY Type, IEEE 802.11-2020 Annex C.
+        self._phy_type = phy_type
+
+    @property
+    def bssid(self) -> str:
+        return self._bssid
+
+    @property
+    def bssid_information(self) -> BssidInformation:
+        return self._bssid_information
+
+    @property
+    def operating_class(self) -> int:
+        return self._operating_class
+
+    @property
+    def channel_number(self) -> int:
+        return self._channel_number
+
+    @property
+    def phy_type(self) -> PhyType:
+        return self._phy_type
diff --git a/acts/framework/acts/controllers/ap_lib/radvd_config.py b/acts/framework/acts/controllers/ap_lib/radvd_config.py
index eb507cb..969d95c 100644
--- a/acts/framework/acts/controllers/ap_lib/radvd_config.py
+++ b/acts/framework/acts/controllers/ap_lib/radvd_config.py
@@ -12,6 +12,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+from acts.controllers.ap_lib import radvd_constants
+
 import collections
 
 
@@ -20,13 +22,14 @@
 
     All the settings for a router advertisement daemon.
     """
+
     def __init__(self,
-                 prefix,
+                 prefix=radvd_constants.DEFAULT_PREFIX,
                  clients=[],
                  route=None,
                  rdnss=[],
                  ignore_if_missing=None,
-                 adv_send_advert=None,
+                 adv_send_advert=radvd_constants.ADV_SEND_ADVERT_ON,
                  unicast_only=None,
                  max_rtr_adv_interval=None,
                  min_rtr_adv_interval=None,
@@ -46,8 +49,8 @@
                  home_agent_preference=None,
                  adv_mob_rtr_support_flag=None,
                  adv_interval_opt=None,
-                 adv_on_link=None,
-                 adv_autonomous=None,
+                 adv_on_link=radvd_constants.ADV_ON_LINK_ON,
+                 adv_autonomous=radvd_constants.ADV_AUTONOMOUS_ON,
                  adv_router_addr=None,
                  adv_valid_lifetime=None,
                  adv_preferred_lifetime=None,
diff --git a/acts/framework/acts/controllers/ap_lib/radvd_constants.py b/acts/framework/acts/controllers/ap_lib/radvd_constants.py
index b83ba37..a1a3e77 100644
--- a/acts/framework/acts/controllers/ap_lib/radvd_constants.py
+++ b/acts/framework/acts/controllers/ap_lib/radvd_constants.py
@@ -14,6 +14,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+DEFAULT_PREFIX = 'fd00::/64'
+
 IGNORE_IF_MISSING_ON = 'on'
 IGNORE_IF_MISSING_OFF = 'off'
 
diff --git a/acts/framework/acts/controllers/ap_lib/wireless_network_management.py b/acts/framework/acts/controllers/ap_lib/wireless_network_management.py
new file mode 100644
index 0000000..f99cd0f
--- /dev/null
+++ b/acts/framework/acts/controllers/ap_lib/wireless_network_management.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from typing import List, NewType, Optional
+
+from acts.controllers.ap_lib.radio_measurement import NeighborReportElement
+
+BssTransitionCandidateList = NewType('BssTransitionCandidateList',
+                                     List[NeighborReportElement])
+
+
+class BssTerminationDuration:
+    """Representation of BSS Termination Duration subelement.
+
+    See IEEE 802.11-2020 Figure 9-341.
+    """
+
+    def __init__(self, duration: int):
+        """Create a BSS Termination Duration subelement.
+
+        Args:
+            duration: number of minutes the BSS will be offline.
+        """
+        # Note: hostapd does not currently support setting BSS Termination TSF,
+        # which is the other value held in this subelement.
+        self._duration = duration
+
+    @property
+    def duration(self) -> int:
+        return self._duration
+
+
+class BssTransitionManagementRequest:
+    """Representation of BSS Transition Management request.
+
+    See IEEE 802.11-2020 9.6.13.9.
+    """
+
+    def __init__(
+            self,
+            preferred_candidate_list_included: bool = False,
+            abridged: bool = False,
+            disassociation_imminent: bool = False,
+            ess_disassociation_imminent: bool = False,
+            disassociation_timer: int = 0,
+            validity_interval: int = 1,
+            bss_termination_duration: Optional[BssTerminationDuration] = None,
+            session_information_url: Optional[str] = None,
+            candidate_list: Optional[BssTransitionCandidateList] = None):
+        """Create a BSS Transition Management request.
+
+        Args:
+            preferred_candidate_list_included: whether the candidate list is a
+                preferred candidate list, or (if False) a list of known
+                candidates.
+            abridged: whether a preference value of 0 is assigned to all BSSIDs
+                that do not appear in the candidate list, or (if False) AP has
+                no recommendation for/against anything not in the candidate
+                list.
+            disassociation_imminent: whether the STA is about to be
+                disassociated by the AP.
+            ess_disassociation_imminent: whether the STA will be disassociated
+                from the ESS.
+            disassociation_timer: the number of beacon transmission times
+                (TBTTs) until the AP disassociates this STA (default 0, meaning
+                AP has not determined when it will disassociate this STA).
+            validity_interval: number of TBTTs until the candidate list is no
+                longer valid (default 1).
+            bss_termination_duration: BSS Termination Duration subelement.
+            session_information_url: this URL is included if ESS disassociation
+                is immiment.
+            candidate_list: zero or more neighbor report elements.
+        """
+        # Request mode field, see IEEE 802.11-2020 Figure 9-924.
+        self._preferred_candidate_list_included = preferred_candidate_list_included
+        self._abridged = abridged
+        self._disassociation_imminent = disassociation_imminent
+        self._ess_disassociation_imminent = ess_disassociation_imminent
+
+        # Disassociation Timer, see IEEE 802.11-2020 Figure 9-925
+        self._disassociation_timer = disassociation_timer
+
+        # Validity Interval, see IEEE 802.11-2020 9.6.13.9
+        self._validity_interval = validity_interval
+
+        # BSS Termination Duration, see IEEE 802.11-2020 9.6.13.9 and Figure 9-341
+        self._bss_termination_duration = bss_termination_duration
+
+        # Session Information URL, see IEEE 802.11-2020 Figure 9-926
+        self._session_information_url = session_information_url
+
+        # BSS Transition Candidate List Entries, IEEE 802.11-2020 9.6.13.9.
+        self._candidate_list = candidate_list
+
+    @property
+    def preferred_candidate_list_included(self) -> bool:
+        return self._preferred_candidate_list_included
+
+    @property
+    def abridged(self) -> bool:
+        return self._abridged
+
+    @property
+    def disassociation_imminent(self) -> bool:
+        return self._disassociation_imminent
+
+    @property
+    def bss_termination_included(self) -> bool:
+        return self._bss_termination_duration is not None
+
+    @property
+    def ess_disassociation_imminent(self) -> bool:
+        return self._ess_disassociation_imminent
+
+    @property
+    def disassociation_timer(self) -> Optional[int]:
+        if self.disassociation_imminent:
+            return self._disassociation_timer
+        # Otherwise, field is reserved.
+        return None
+
+    @property
+    def validity_interval(self) -> int:
+        return self._validity_interval
+
+    @property
+    def bss_termination_duration(self) -> Optional[BssTerminationDuration]:
+        return self._bss_termination_duration
+
+    @property
+    def session_information_url(self) -> Optional[str]:
+        return self._session_information_url
+
+    @property
+    def candidate_list(self) -> Optional[BssTransitionCandidateList]:
+        return self._candidate_list
diff --git a/acts/framework/acts/controllers/attenuator.py b/acts/framework/acts/controllers/attenuator.py
index b7f4a6d..cceb857 100644
--- a/acts/framework/acts/controllers/attenuator.py
+++ b/acts/framework/acts/controllers/attenuator.py
@@ -181,7 +181,6 @@
     attenuator instrument is probably necessary. Something has gone wrong in
     the transport.
     """
-    pass
 
 
 class InvalidOperationError(AttenuatorError):
@@ -191,7 +190,6 @@
     invoked is in a certain state. This indicates that the object is not in the
     correct state for a method to be called.
     """
-    pass
 
 
 class AttenuatorInstrument(object):
diff --git a/acts/framework/acts/controllers/attenuator_lib/minicircuits/http.py b/acts/framework/acts/controllers/attenuator_lib/minicircuits/http.py
index bd3d24c..62dc330 100644
--- a/acts/framework/acts/controllers/attenuator_lib/minicircuits/http.py
+++ b/acts/framework/acts/controllers/attenuator_lib/minicircuits/http.py
@@ -35,6 +35,7 @@
     With the exception of HTTP-specific commands, all functionality is defined
     by the AttenuatorInstrument class.
     """
+
     def __init__(self, num_atten=1):
         super(AttenuatorInstrument, self).__init__(num_atten)
         self._ip_address = None
@@ -83,7 +84,6 @@
         Since this controller is based on HTTP requests, there is no connection
         teardowns required.
         """
-        pass
 
     def set_atten(self, idx, value, strict=True, retry=False, **_):
         """This function sets the attenuation of an attenuator given its index
@@ -144,8 +144,7 @@
             raise IndexError('Attenuator index out of range!', self.num_atten,
                              idx)
         att_req = urllib.request.urlopen(
-            'http://{}:{}/CHAN:{}:ATT?'.format(self._ip_address, self.port,
-                                               idx + 1),
+            'http://{}:{}/CHAN:{}:ATT?'.format(self._ip_address, self.port, idx + 1),
             timeout=self._timeout)
         att_resp = att_req.read().decode('utf-8').strip()
         try:
diff --git a/acts/framework/acts/controllers/bits.py b/acts/framework/acts/controllers/bits.py
index d89a9b3..caadeb7 100644
--- a/acts/framework/acts/controllers/bits.py
+++ b/acts/framework/acts/controllers/bits.py
@@ -1,5 +1,7 @@
 """Module managing the required definitions for using the bits power monitor"""
 
+import csv
+import json
 import logging
 import os
 import time
@@ -424,8 +426,14 @@
         In the case where there is not enough information to retrieve a
         monsoon-like file, this function will do nothing.
         """
-        available_channels = self._client.list_channels(
-            self._active_collection.name)
+        metrics = self._client.get_metrics(self._active_collection.name)
+
+        try:
+            self._save_rails_csv(metrics)
+        except Exception as e:
+            logging.warning(
+                'Could not save rails data to csv format with error {}'.format(e))
+        available_channels = [channel['name'] for channel in metrics['data']]
         milli_amps_channel = None
 
         for channel in available_channels:
@@ -447,6 +455,70 @@
             self._active_collection.name,
             milli_amps_channel)
 
+    def _save_rails_csv(self, metrics):
+        # Creates csv path for rails data
+        monsoon_path = self._active_collection.monsoon_output_path
+        dir_path = os.path.dirname(monsoon_path)
+        if dir_path.endswith('Monsoon'):
+            dir_path = os.path.join(os.path.dirname(dir_path), 'Kibble')
+            os.makedirs(dir_path, exist_ok=True)
+        rails_basename = os.path.basename(monsoon_path)
+        if rails_basename.endswith('.txt'):
+            rails_basename = os.path.splitext(rails_basename)[0]
+        json_basename = 'kibble_rails_' + rails_basename + '.json'
+        rails_basename = 'kibble_rails_' + rails_basename + '.csv'
+        root_rail_results_basename = '{}_results.csv'.format(
+            self._root_rail.split(':')[0])
+        rails_csv_path = os.path.join(dir_path, rails_basename)
+        rails_json_path = os.path.join(dir_path, json_basename)
+        root_rail_results_path = os.path.join(dir_path, root_rail_results_basename)
+
+        logging.info('dump metric to json format: {}'.format(rails_json_path))
+        with open(rails_json_path, 'w') as f:
+            json.dump(metrics['data'], f, sort_keys=True, indent=2)
+
+        # Gets all channels
+        channels = {
+            channel['name'].split('.')[-1].split(':')[0]
+            for channel in metrics['data']
+        }
+        channels = list(channels)
+        list.sort(channels)
+
+        rail_dict = {
+            channel['name'].split('.')[-1] : channel['avg']
+            for channel in metrics['data']
+        }
+
+        root_rail_key = self._root_rail.split(':')[0] + ':mW'
+        root_rail_power = 0
+        if root_rail_key in rail_dict:
+            root_rail_power = rail_dict[root_rail_key]
+        logging.info('root rail {} power is: {}'.format(root_rail_key, root_rail_power))
+
+        path_existed = os.path.exists(root_rail_results_path)
+        with open(root_rail_results_path, 'a') as f:
+            if not path_existed:
+                f.write('{},{}'.format(root_rail_key, 'power(mW)'))
+            f.write('\n{},{}'.format(self._active_collection.name, root_rail_power))
+
+        header = ['CHANNEL', 'VALUE', 'UNIT', 'VALUE', 'UNIT', 'VALUE', 'UNIT']
+        with open(rails_csv_path, 'w') as f:
+            csvwriter = csv.writer(f)
+            csvwriter.writerow(header)
+            for key in  channels:
+                if not key.startswith('C') and not key.startswith('M'):
+                    continue
+                try:
+                    row = [key, '0', 'mA', '0', 'mV', '0', 'mW']
+                    row[1] = str(rail_dict[key + ':mA'])
+                    row[3] = str(rail_dict[key + ':mV'])
+                    row[5] = str(rail_dict[key + ':mW'])
+                    csvwriter.writerow(row)
+                    logging.debug('channel {}: {}'.format(key, row))
+                except Exception as e:
+                    logging.info('channel {} fail'.format(key))
+
     def get_waveform(self, file_path=None):
         """Parses a file generated in release_resources.
 
@@ -462,6 +534,26 @@
 
         return list(power_metrics.import_raw_data(file_path))
 
+    def get_bits_root_rail_csv_export(self, file_path=None, collection_name=None):
+        """Export raw data samples for root rail in csv format.
+
+        Args:
+            file_path: Path to save the export file.
+            collection_name: Name of collection to be exported on client.
+        """
+        if file_path is None:
+            raise ValueError('file_path cannot be None')
+        if collection_name is None:
+            raise ValueError('collection_name cannot be None')
+        try:
+            key = self._root_rail.split(':')[0] + ':mW'
+            file_name = 'raw_data_' + collection_name + '.csv'
+            raw_bits_data_path = os.path.join(file_path, file_name)
+            self._client.export_as_csv([key], collection_name,
+                                       raw_bits_data_path)
+        except Exception as e:
+            logging.warning('Failed to save raw data due to :  {}'.format(e))
+
     def teardown(self):
         if self._service is None:
             return
diff --git a/acts/framework/acts/controllers/bits_lib/bits_service_config.py b/acts/framework/acts/controllers/bits_lib/bits_service_config.py
index cb2d219..a5fb2f9 100644
--- a/acts/framework/acts/controllers/bits_lib/bits_service_config.py
+++ b/acts/framework/acts/controllers/bits_lib/bits_service_config.py
@@ -144,7 +144,11 @@
             if 'serial' not in kibble:
                 raise ValueError('An individual kibble config must have a '
                                  'serial')
-
+            if 'subkibble_params' in kibble:
+                user_defined_kibble_config = kibble['subkibble_params']
+                kibble_config = copy.deepcopy(user_defined_kibble_config)
+            else:
+                kibble_config = copy.deepcopy(DEFAULT_KIBBLE_CONFIG)
             board = kibble['board']
             connector = kibble['connector']
             serial = kibble['serial']
@@ -154,7 +158,6 @@
                 self.boards_configs[board][
                     'board_file'] = kibble_board_file
                 self.boards_configs[board]['kibble_py'] = kibble_bin
-            kibble_config = copy.deepcopy(DEFAULT_KIBBLE_CONFIG)
             kibble_config['connector'] = connector
             self.boards_configs[board]['attached_kibbles'][
                 serial] = kibble_config
diff --git a/acts/framework/acts/controllers/bluetooth_pts_device.py b/acts/framework/acts/controllers/bluetooth_pts_device.py
index 917c1e4..f321141 100644
--- a/acts/framework/acts/controllers/bluetooth_pts_device.py
+++ b/acts/framework/acts/controllers/bluetooth_pts_device.py
@@ -47,7 +47,6 @@
 """
 from acts import signals
 from datetime import datetime
-from threading import Thread
 
 import ctypes
 import logging
diff --git a/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/apollo_qa_pb2.py b/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/apollo_qa_pb2.py
index 8bfbda9..1290491 100644
--- a/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/apollo_qa_pb2.py
+++ b/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/apollo_qa_pb2.py
@@ -1,807 +1,59 @@
+# -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: apollo_qa.proto
-
-import sys
-_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
-from google.protobuf.internal import enum_type_wrapper
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
-from google.protobuf import descriptor_pb2
 # @@protoc_insertion_point(imports)
 
 _sym_db = _symbol_database.Default()
 
 
-import acts.controllers.buds_lib.dev_utils.proto.gen.nanopb_pb2 as nanopb__pb2
+from . import nanopb_pb2 as nanopb__pb2
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='apollo_qa.proto',
-  package='apollo.lib.apollo_dev_util_lib.proto',
-  syntax='proto2',
-  serialized_pb=_b('\n\x0f\x61pollo_qa.proto\x12$apollo.lib.apollo_dev_util_lib.proto\x1a\x0cnanopb.proto\"t\n\rApolloQATrace\x12\x11\n\ttimestamp\x18\x01 \x02(\r\x12\x39\n\x02id\x18\x02 \x02(\x0e\x32-.apollo.lib.apollo_dev_util_lib.proto.TraceId\x12\x15\n\x04\x64\x61ta\x18\x03 \x03(\rB\x07\x10\x01\x92?\x02\x10\x05\"\xcd\x02\n\x16\x41polloQAGetVerResponse\x12\x11\n\ttimestamp\x18\x01 \x02(\r\x12\x16\n\x0e\x63sr_fw_version\x18\x02 \x02(\r\x12\x1a\n\x12\x63sr_fw_debug_build\x18\x03 \x02(\x08\x12\x17\n\x0fvm_build_number\x18\x04 \x02(\r\x12\x16\n\x0evm_debug_build\x18\x05 \x02(\x08\x12\x14\n\x0cpsoc_version\x18\x06 \x02(\r\x12\x1a\n\x0b\x62uild_label\x18\x07 \x02(\tB\x05\x92?\x02\x08 \x12Q\n\x0flast_ota_status\x18\x08 \x01(\x0e\x32\x38.apollo.lib.apollo_dev_util_lib.proto.PreviousBootStatus\x12\x17\n\x0f\x63harger_version\x18\t \x02(\r\x12\x1d\n\x15\x65xpected_psoc_version\x18\n \x01(\r\"u\n\x18\x41polloQAGetCodecResponse\x12\x11\n\ttimestamp\x18\x01 \x02(\r\x12\x46\n\x05\x63odec\x18\x02 \x01(\x0e\x32\x37.apollo.lib.apollo_dev_util_lib.proto.ApolloQAA2dpCodec\"\xa6\x01\n\x1c\x41polloQAGetDspStatusResponse\x12\x11\n\ttimestamp\x18\x01 \x02(\r\x12\x15\n\ris_dsp_loaded\x18\x02 \x02(\x08\x12\x43\n\nsink_state\x18\x03 \x02(\x0e\x32/.apollo.lib.apollo_dev_util_lib.proto.SinkState\x12\x17\n\x0f\x66\x65\x61tures_active\x18\x04 \x02(\r\"\xb9\x01\n\x18\x41polloQAFactoryPlaySound\x12Y\n\x06prompt\x18\x01 \x02(\x0e\x32I.apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryPlaySound.PromptType\"B\n\nPromptType\x12\x1c\n\x18PROMPT_TYPE_BT_CONNECTED\x10\x01\x12\x16\n\x12PROMPT_TYPE_IN_EAR\x10\x02\"\x1c\n\x1a\x41polloQAFactoryInfoRequest\"\xb6\x01\n\x1b\x41polloQAFactoryInfoResponse\x12\x11\n\ttimestamp\x18\x01 \x02(\r\x12\x1b\n\x0c\x63rystal_trim\x18\x02 \x01(\x05\x42\x05\x92?\x02\x38\x10\x12\x19\n\x11\x63rash_dump_exists\x18\x03 \x01(\x08\x12!\n\x19is_developer_mode_enabled\x18\x04 \x01(\x08\x12\x1b\n\x13is_always_connected\x18\x05 \x01(\x08\x12\x0c\n\x04hwid\x18\x06 \x01(\r*\xb8\x01\n\x13\x41polloQAMessageType\x12\t\n\x05TRACE\x10\x01\x12\x14\n\x10GET_VER_RESPONSE\x10\x02\x12\x16\n\x12GET_CODEC_RESPONSE\x10\x03\x12\x1b\n\x17GET_DSP_STATUS_RESPONSE\x10\x04\x12\x16\n\x12\x46\x41\x43TORY_PLAY_SOUND\x10\x05\x12\x18\n\x14\x46\x41\x43TORY_INFO_REQUEST\x10\x06\x12\x19\n\x15\x46\x41\x43TORY_INFO_RESPONSE\x10\x07*\xfc\x02\n\x07TraceId\x12\x17\n\x13OTA_ERASE_PARTITION\x10\x01\x12\x1d\n\x19OTA_START_PARTITION_WRITE\x10\x02\x12 \n\x1cOTA_FINISHED_PARTITION_WRITE\x10\x03\x12\x17\n\x13OTA_SIGNATURE_START\x10\x04\x12\x19\n\x15OTA_SIGNATURE_FAILURE\x10\x05\x12\x19\n\x15OTA_TRIGGERING_LOADER\x10\x06\x12\x1c\n\x18OTA_LOADER_VERIFY_FAILED\x10\x07\x12\x10\n\x0cOTA_PROGRESS\x10\x08\x12\x0f\n\x0bOTA_ABORTED\x10\t\x12\x1c\n\x18\x41VRCP_PLAY_STATUS_CHANGE\x10\n\x12\x11\n\rVOLUME_CHANGE\x10\x0b\x12\x1a\n\x16\x43OMMANDER_RECV_COMMAND\x10\x0c\x12\x1c\n\x18\x43OMMANDER_FINISH_COMMAND\x10\r\x12\x1c\n\x18\x43OMMANDER_REJECT_COMMAND\x10\x0e*m\n\x0f\x41vrcpPlayStatus\x12\x0b\n\x07STOPPED\x10\x00\x12\x0b\n\x07PLAYING\x10\x01\x12\n\n\x06PAUSED\x10\x02\x12\x0c\n\x08\x46WD_SEEK\x10\x08\x12\x0c\n\x08REV_SEEK\x10\x10\x12\t\n\x05\x45RROR\x10\x05\x12\r\n\tSEEK_MASK\x10\x18*4\n\x12PreviousBootStatus\x12\x0f\n\x0bOTA_SUCCESS\x10\x01\x12\r\n\tOTA_ERROR\x10\x02*%\n\x11\x41polloQAA2dpCodec\x12\x07\n\x03\x41\x41\x43\x10\x01\x12\x07\n\x03SBC\x10\x02*\xd8\x02\n\tSinkState\x12\t\n\x05LIMBO\x10\x00\x12\x0f\n\x0b\x43ONNECTABLE\x10\x01\x12\x10\n\x0c\x44ISCOVERABLE\x10\x02\x12\r\n\tCONNECTED\x10\x03\x12\x1c\n\x18OUTGOING_CALLS_ESTABLISH\x10\x04\x12\x1c\n\x18INCOMING_CALLS_ESTABLISH\x10\x05\x12\x13\n\x0f\x41\x43TIVE_CALL_SCO\x10\x06\x12\r\n\tTEST_MODE\x10\x07\x12\x1a\n\x16THREE_WAY_CALL_WAITING\x10\x08\x12\x1a\n\x16THREE_WAY_CALL_ON_HOLD\x10\t\x12\x17\n\x13THREE_WAY_MULTICALL\x10\n\x12\x19\n\x15INCOMING_CALL_ON_HOLD\x10\x0b\x12\x16\n\x12\x41\x43TIVE_CALL_NO_SCO\x10\x0c\x12\x12\n\x0e\x41\x32\x44P_STREAMING\x10\r\x12\x16\n\x12\x44\x45VICE_LOW_BATTERY\x10\x0e\x42)\n\x1d\x63om.google.android.bisto.nanoB\x08\x41polloQA')
-  ,
-  dependencies=[nanopb__pb2.DESCRIPTOR,])
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0f\x61pollo_qa.proto\x12$apollo.lib.apollo_dev_util_lib.proto\x1a\x0cnanopb.proto\"t\n\rApolloQATrace\x12\x11\n\ttimestamp\x18\x01 \x02(\r\x12\x39\n\x02id\x18\x02 \x02(\x0e\x32-.apollo.lib.apollo_dev_util_lib.proto.TraceId\x12\x15\n\x04\x64\x61ta\x18\x03 \x03(\rB\x07\x10\x01\x92?\x02\x10\x05\"\xcd\x02\n\x16\x41polloQAGetVerResponse\x12\x11\n\ttimestamp\x18\x01 \x02(\r\x12\x16\n\x0e\x63sr_fw_version\x18\x02 \x02(\r\x12\x1a\n\x12\x63sr_fw_debug_build\x18\x03 \x02(\x08\x12\x17\n\x0fvm_build_number\x18\x04 \x02(\r\x12\x16\n\x0evm_debug_build\x18\x05 \x02(\x08\x12\x14\n\x0cpsoc_version\x18\x06 \x02(\r\x12\x1a\n\x0b\x62uild_label\x18\x07 \x02(\tB\x05\x92?\x02\x08 \x12Q\n\x0flast_ota_status\x18\x08 \x01(\x0e\x32\x38.apollo.lib.apollo_dev_util_lib.proto.PreviousBootStatus\x12\x17\n\x0f\x63harger_version\x18\t \x02(\r\x12\x1d\n\x15\x65xpected_psoc_version\x18\n \x01(\r\"u\n\x18\x41polloQAGetCodecResponse\x12\x11\n\ttimestamp\x18\x01 \x02(\r\x12\x46\n\x05\x63odec\x18\x02 \x01(\x0e\x32\x37.apollo.lib.apollo_dev_util_lib.proto.ApolloQAA2dpCodec\"\xa6\x01\n\x1c\x41polloQAGetDspStatusResponse\x12\x11\n\ttimestamp\x18\x01 \x02(\r\x12\x15\n\ris_dsp_loaded\x18\x02 \x02(\x08\x12\x43\n\nsink_state\x18\x03 \x02(\x0e\x32/.apollo.lib.apollo_dev_util_lib.proto.SinkState\x12\x17\n\x0f\x66\x65\x61tures_active\x18\x04 \x02(\r\"\xb9\x01\n\x18\x41polloQAFactoryPlaySound\x12Y\n\x06prompt\x18\x01 \x02(\x0e\x32I.apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryPlaySound.PromptType\"B\n\nPromptType\x12\x1c\n\x18PROMPT_TYPE_BT_CONNECTED\x10\x01\x12\x16\n\x12PROMPT_TYPE_IN_EAR\x10\x02\"\x1c\n\x1a\x41polloQAFactoryInfoRequest\"\xb6\x01\n\x1b\x41polloQAFactoryInfoResponse\x12\x11\n\ttimestamp\x18\x01 \x02(\r\x12\x1b\n\x0c\x63rystal_trim\x18\x02 \x01(\x05\x42\x05\x92?\x02\x38\x10\x12\x19\n\x11\x63rash_dump_exists\x18\x03 \x01(\x08\x12!\n\x19is_developer_mode_enabled\x18\x04 \x01(\x08\x12\x1b\n\x13is_always_connected\x18\x05 \x01(\x08\x12\x0c\n\x04hwid\x18\x06 \x01(\r*\xb8\x01\n\x13\x41polloQAMessageType\x12\t\n\x05TRACE\x10\x01\x12\x14\n\x10GET_VER_RESPONSE\x10\x02\x12\x16\n\x12GET_CODEC_RESPONSE\x10\x03\x12\x1b\n\x17GET_DSP_STATUS_RESPONSE\x10\x04\x12\x16\n\x12\x46\x41\x43TORY_PLAY_SOUND\x10\x05\x12\x18\n\x14\x46\x41\x43TORY_INFO_REQUEST\x10\x06\x12\x19\n\x15\x46\x41\x43TORY_INFO_RESPONSE\x10\x07*\xfc\x02\n\x07TraceId\x12\x17\n\x13OTA_ERASE_PARTITION\x10\x01\x12\x1d\n\x19OTA_START_PARTITION_WRITE\x10\x02\x12 \n\x1cOTA_FINISHED_PARTITION_WRITE\x10\x03\x12\x17\n\x13OTA_SIGNATURE_START\x10\x04\x12\x19\n\x15OTA_SIGNATURE_FAILURE\x10\x05\x12\x19\n\x15OTA_TRIGGERING_LOADER\x10\x06\x12\x1c\n\x18OTA_LOADER_VERIFY_FAILED\x10\x07\x12\x10\n\x0cOTA_PROGRESS\x10\x08\x12\x0f\n\x0bOTA_ABORTED\x10\t\x12\x1c\n\x18\x41VRCP_PLAY_STATUS_CHANGE\x10\n\x12\x11\n\rVOLUME_CHANGE\x10\x0b\x12\x1a\n\x16\x43OMMANDER_RECV_COMMAND\x10\x0c\x12\x1c\n\x18\x43OMMANDER_FINISH_COMMAND\x10\r\x12\x1c\n\x18\x43OMMANDER_REJECT_COMMAND\x10\x0e*m\n\x0f\x41vrcpPlayStatus\x12\x0b\n\x07STOPPED\x10\x00\x12\x0b\n\x07PLAYING\x10\x01\x12\n\n\x06PAUSED\x10\x02\x12\x0c\n\x08\x46WD_SEEK\x10\x08\x12\x0c\n\x08REV_SEEK\x10\x10\x12\t\n\x05\x45RROR\x10\x05\x12\r\n\tSEEK_MASK\x10\x18*4\n\x12PreviousBootStatus\x12\x0f\n\x0bOTA_SUCCESS\x10\x01\x12\r\n\tOTA_ERROR\x10\x02*%\n\x11\x41polloQAA2dpCodec\x12\x07\n\x03\x41\x41\x43\x10\x01\x12\x07\n\x03SBC\x10\x02*\xd8\x02\n\tSinkState\x12\t\n\x05LIMBO\x10\x00\x12\x0f\n\x0b\x43ONNECTABLE\x10\x01\x12\x10\n\x0c\x44ISCOVERABLE\x10\x02\x12\r\n\tCONNECTED\x10\x03\x12\x1c\n\x18OUTGOING_CALLS_ESTABLISH\x10\x04\x12\x1c\n\x18INCOMING_CALLS_ESTABLISH\x10\x05\x12\x13\n\x0f\x41\x43TIVE_CALL_SCO\x10\x06\x12\r\n\tTEST_MODE\x10\x07\x12\x1a\n\x16THREE_WAY_CALL_WAITING\x10\x08\x12\x1a\n\x16THREE_WAY_CALL_ON_HOLD\x10\t\x12\x17\n\x13THREE_WAY_MULTICALL\x10\n\x12\x19\n\x15INCOMING_CALL_ON_HOLD\x10\x0b\x12\x16\n\x12\x41\x43TIVE_CALL_NO_SCO\x10\x0c\x12\x12\n\x0e\x41\x32\x44P_STREAMING\x10\r\x12\x16\n\x12\x44\x45VICE_LOW_BATTERY\x10\x0e\x42)\n\x1d\x63om.google.android.bisto.nanoB\x08\x41polloQA')
 
-_APOLLOQAMESSAGETYPE = _descriptor.EnumDescriptor(
-  name='ApolloQAMessageType',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAMessageType',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='TRACE', index=0, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GET_VER_RESPONSE', index=1, number=2,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GET_CODEC_RESPONSE', index=2, number=3,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='GET_DSP_STATUS_RESPONSE', index=3, number=4,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='FACTORY_PLAY_SOUND', index=4, number=5,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='FACTORY_INFO_REQUEST', index=5, number=6,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='FACTORY_INFO_RESPONSE', index=6, number=7,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=1217,
-  serialized_end=1401,
-)
-_sym_db.RegisterEnumDescriptor(_APOLLOQAMESSAGETYPE)
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'apollo_qa_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-ApolloQAMessageType = enum_type_wrapper.EnumTypeWrapper(_APOLLOQAMESSAGETYPE)
-_TRACEID = _descriptor.EnumDescriptor(
-  name='TraceId',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.TraceId',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='OTA_ERASE_PARTITION', index=0, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OTA_START_PARTITION_WRITE', index=1, number=2,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OTA_FINISHED_PARTITION_WRITE', index=2, number=3,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OTA_SIGNATURE_START', index=3, number=4,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OTA_SIGNATURE_FAILURE', index=4, number=5,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OTA_TRIGGERING_LOADER', index=5, number=6,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OTA_LOADER_VERIFY_FAILED', index=6, number=7,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OTA_PROGRESS', index=7, number=8,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OTA_ABORTED', index=8, number=9,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='AVRCP_PLAY_STATUS_CHANGE', index=9, number=10,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='VOLUME_CHANGE', index=10, number=11,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='COMMANDER_RECV_COMMAND', index=11, number=12,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='COMMANDER_FINISH_COMMAND', index=12, number=13,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='COMMANDER_REJECT_COMMAND', index=13, number=14,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=1404,
-  serialized_end=1784,
-)
-_sym_db.RegisterEnumDescriptor(_TRACEID)
-
-TraceId = enum_type_wrapper.EnumTypeWrapper(_TRACEID)
-_AVRCPPLAYSTATUS = _descriptor.EnumDescriptor(
-  name='AvrcpPlayStatus',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.AvrcpPlayStatus',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='STOPPED', index=0, number=0,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='PLAYING', index=1, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='PAUSED', index=2, number=2,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='FWD_SEEK', index=3, number=8,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='REV_SEEK', index=4, number=16,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ERROR', index=5, number=5,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='SEEK_MASK', index=6, number=24,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=1786,
-  serialized_end=1895,
-)
-_sym_db.RegisterEnumDescriptor(_AVRCPPLAYSTATUS)
-
-AvrcpPlayStatus = enum_type_wrapper.EnumTypeWrapper(_AVRCPPLAYSTATUS)
-_PREVIOUSBOOTSTATUS = _descriptor.EnumDescriptor(
-  name='PreviousBootStatus',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.PreviousBootStatus',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='OTA_SUCCESS', index=0, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OTA_ERROR', index=1, number=2,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=1897,
-  serialized_end=1949,
-)
-_sym_db.RegisterEnumDescriptor(_PREVIOUSBOOTSTATUS)
-
-PreviousBootStatus = enum_type_wrapper.EnumTypeWrapper(_PREVIOUSBOOTSTATUS)
-_APOLLOQAA2DPCODEC = _descriptor.EnumDescriptor(
-  name='ApolloQAA2dpCodec',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAA2dpCodec',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='AAC', index=0, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='SBC', index=1, number=2,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=1951,
-  serialized_end=1988,
-)
-_sym_db.RegisterEnumDescriptor(_APOLLOQAA2DPCODEC)
-
-ApolloQAA2dpCodec = enum_type_wrapper.EnumTypeWrapper(_APOLLOQAA2DPCODEC)
-_SINKSTATE = _descriptor.EnumDescriptor(
-  name='SinkState',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.SinkState',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='LIMBO', index=0, number=0,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='CONNECTABLE', index=1, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DISCOVERABLE', index=2, number=2,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='CONNECTED', index=3, number=3,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OUTGOING_CALLS_ESTABLISH', index=4, number=4,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='INCOMING_CALLS_ESTABLISH', index=5, number=5,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ACTIVE_CALL_SCO', index=6, number=6,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TEST_MODE', index=7, number=7,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='THREE_WAY_CALL_WAITING', index=8, number=8,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='THREE_WAY_CALL_ON_HOLD', index=9, number=9,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='THREE_WAY_MULTICALL', index=10, number=10,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='INCOMING_CALL_ON_HOLD', index=11, number=11,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ACTIVE_CALL_NO_SCO', index=12, number=12,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='A2DP_STREAMING', index=13, number=13,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DEVICE_LOW_BATTERY', index=14, number=14,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=1991,
-  serialized_end=2335,
-)
-_sym_db.RegisterEnumDescriptor(_SINKSTATE)
-
-SinkState = enum_type_wrapper.EnumTypeWrapper(_SINKSTATE)
-TRACE = 1
-GET_VER_RESPONSE = 2
-GET_CODEC_RESPONSE = 3
-GET_DSP_STATUS_RESPONSE = 4
-FACTORY_PLAY_SOUND = 5
-FACTORY_INFO_REQUEST = 6
-FACTORY_INFO_RESPONSE = 7
-OTA_ERASE_PARTITION = 1
-OTA_START_PARTITION_WRITE = 2
-OTA_FINISHED_PARTITION_WRITE = 3
-OTA_SIGNATURE_START = 4
-OTA_SIGNATURE_FAILURE = 5
-OTA_TRIGGERING_LOADER = 6
-OTA_LOADER_VERIFY_FAILED = 7
-OTA_PROGRESS = 8
-OTA_ABORTED = 9
-AVRCP_PLAY_STATUS_CHANGE = 10
-VOLUME_CHANGE = 11
-COMMANDER_RECV_COMMAND = 12
-COMMANDER_FINISH_COMMAND = 13
-COMMANDER_REJECT_COMMAND = 14
-STOPPED = 0
-PLAYING = 1
-PAUSED = 2
-FWD_SEEK = 8
-REV_SEEK = 16
-ERROR = 5
-SEEK_MASK = 24
-OTA_SUCCESS = 1
-OTA_ERROR = 2
-AAC = 1
-SBC = 2
-LIMBO = 0
-CONNECTABLE = 1
-DISCOVERABLE = 2
-CONNECTED = 3
-OUTGOING_CALLS_ESTABLISH = 4
-INCOMING_CALLS_ESTABLISH = 5
-ACTIVE_CALL_SCO = 6
-TEST_MODE = 7
-THREE_WAY_CALL_WAITING = 8
-THREE_WAY_CALL_ON_HOLD = 9
-THREE_WAY_MULTICALL = 10
-INCOMING_CALL_ON_HOLD = 11
-ACTIVE_CALL_NO_SCO = 12
-A2DP_STREAMING = 13
-DEVICE_LOW_BATTERY = 14
-
-
-_APOLLOQAFACTORYPLAYSOUND_PROMPTTYPE = _descriptor.EnumDescriptor(
-  name='PromptType',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryPlaySound.PromptType',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='PROMPT_TYPE_BT_CONNECTED', index=0, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='PROMPT_TYPE_IN_EAR', index=1, number=2,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=933,
-  serialized_end=999,
-)
-_sym_db.RegisterEnumDescriptor(_APOLLOQAFACTORYPLAYSOUND_PROMPTTYPE)
-
-
-_APOLLOQATRACE = _descriptor.Descriptor(
-  name='ApolloQATrace',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQATrace',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='timestamp', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQATrace.timestamp', index=0,
-      number=1, type=13, cpp_type=3, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='id', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQATrace.id', index=1,
-      number=2, type=14, cpp_type=8, label=2,
-      has_default_value=False, default_value=1,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='data', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQATrace.data', index=2,
-      number=3, type=13, cpp_type=3, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001\222?\002\020\005'))),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=71,
-  serialized_end=187,
-)
-
-
-_APOLLOQAGETVERRESPONSE = _descriptor.Descriptor(
-  name='ApolloQAGetVerResponse',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='timestamp', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse.timestamp', index=0,
-      number=1, type=13, cpp_type=3, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='csr_fw_version', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse.csr_fw_version', index=1,
-      number=2, type=13, cpp_type=3, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='csr_fw_debug_build', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse.csr_fw_debug_build', index=2,
-      number=3, type=8, cpp_type=7, label=2,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='vm_build_number', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse.vm_build_number', index=3,
-      number=4, type=13, cpp_type=3, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='vm_debug_build', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse.vm_debug_build', index=4,
-      number=5, type=8, cpp_type=7, label=2,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='psoc_version', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse.psoc_version', index=5,
-      number=6, type=13, cpp_type=3, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='build_label', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse.build_label', index=6,
-      number=7, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\222?\002\010 '))),
-    _descriptor.FieldDescriptor(
-      name='last_ota_status', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse.last_ota_status', index=7,
-      number=8, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=1,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='charger_version', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse.charger_version', index=8,
-      number=9, type=13, cpp_type=3, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='expected_psoc_version', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse.expected_psoc_version', index=9,
-      number=10, type=13, cpp_type=3, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=190,
-  serialized_end=523,
-)
-
-
-_APOLLOQAGETCODECRESPONSE = _descriptor.Descriptor(
-  name='ApolloQAGetCodecResponse',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetCodecResponse',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='timestamp', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetCodecResponse.timestamp', index=0,
-      number=1, type=13, cpp_type=3, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='codec', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetCodecResponse.codec', index=1,
-      number=2, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=1,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=525,
-  serialized_end=642,
-)
-
-
-_APOLLOQAGETDSPSTATUSRESPONSE = _descriptor.Descriptor(
-  name='ApolloQAGetDspStatusResponse',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetDspStatusResponse',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='timestamp', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetDspStatusResponse.timestamp', index=0,
-      number=1, type=13, cpp_type=3, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='is_dsp_loaded', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetDspStatusResponse.is_dsp_loaded', index=1,
-      number=2, type=8, cpp_type=7, label=2,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='sink_state', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetDspStatusResponse.sink_state', index=2,
-      number=3, type=14, cpp_type=8, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='features_active', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetDspStatusResponse.features_active', index=3,
-      number=4, type=13, cpp_type=3, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=645,
-  serialized_end=811,
-)
-
-
-_APOLLOQAFACTORYPLAYSOUND = _descriptor.Descriptor(
-  name='ApolloQAFactoryPlaySound',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryPlaySound',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='prompt', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryPlaySound.prompt', index=0,
-      number=1, type=14, cpp_type=8, label=2,
-      has_default_value=False, default_value=1,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _APOLLOQAFACTORYPLAYSOUND_PROMPTTYPE,
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=814,
-  serialized_end=999,
-)
-
-
-_APOLLOQAFACTORYINFOREQUEST = _descriptor.Descriptor(
-  name='ApolloQAFactoryInfoRequest',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryInfoRequest',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1001,
-  serialized_end=1029,
-)
-
-
-_APOLLOQAFACTORYINFORESPONSE = _descriptor.Descriptor(
-  name='ApolloQAFactoryInfoResponse',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryInfoResponse',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='timestamp', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryInfoResponse.timestamp', index=0,
-      number=1, type=13, cpp_type=3, label=2,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='crystal_trim', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryInfoResponse.crystal_trim', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=_descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\222?\0028\020'))),
-    _descriptor.FieldDescriptor(
-      name='crash_dump_exists', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryInfoResponse.crash_dump_exists', index=2,
-      number=3, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='is_developer_mode_enabled', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryInfoResponse.is_developer_mode_enabled', index=3,
-      number=4, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='is_always_connected', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryInfoResponse.is_always_connected', index=4,
-      number=5, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='hwid', full_name='apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryInfoResponse.hwid', index=5,
-      number=6, type=13, cpp_type=3, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1032,
-  serialized_end=1214,
-)
-
-_APOLLOQATRACE.fields_by_name['id'].enum_type = _TRACEID
-_APOLLOQAGETVERRESPONSE.fields_by_name['last_ota_status'].enum_type = _PREVIOUSBOOTSTATUS
-_APOLLOQAGETCODECRESPONSE.fields_by_name['codec'].enum_type = _APOLLOQAA2DPCODEC
-_APOLLOQAGETDSPSTATUSRESPONSE.fields_by_name['sink_state'].enum_type = _SINKSTATE
-_APOLLOQAFACTORYPLAYSOUND.fields_by_name['prompt'].enum_type = _APOLLOQAFACTORYPLAYSOUND_PROMPTTYPE
-_APOLLOQAFACTORYPLAYSOUND_PROMPTTYPE.containing_type = _APOLLOQAFACTORYPLAYSOUND
-DESCRIPTOR.message_types_by_name['ApolloQATrace'] = _APOLLOQATRACE
-DESCRIPTOR.message_types_by_name['ApolloQAGetVerResponse'] = _APOLLOQAGETVERRESPONSE
-DESCRIPTOR.message_types_by_name['ApolloQAGetCodecResponse'] = _APOLLOQAGETCODECRESPONSE
-DESCRIPTOR.message_types_by_name['ApolloQAGetDspStatusResponse'] = _APOLLOQAGETDSPSTATUSRESPONSE
-DESCRIPTOR.message_types_by_name['ApolloQAFactoryPlaySound'] = _APOLLOQAFACTORYPLAYSOUND
-DESCRIPTOR.message_types_by_name['ApolloQAFactoryInfoRequest'] = _APOLLOQAFACTORYINFOREQUEST
-DESCRIPTOR.message_types_by_name['ApolloQAFactoryInfoResponse'] = _APOLLOQAFACTORYINFORESPONSE
-DESCRIPTOR.enum_types_by_name['ApolloQAMessageType'] = _APOLLOQAMESSAGETYPE
-DESCRIPTOR.enum_types_by_name['TraceId'] = _TRACEID
-DESCRIPTOR.enum_types_by_name['AvrcpPlayStatus'] = _AVRCPPLAYSTATUS
-DESCRIPTOR.enum_types_by_name['PreviousBootStatus'] = _PREVIOUSBOOTSTATUS
-DESCRIPTOR.enum_types_by_name['ApolloQAA2dpCodec'] = _APOLLOQAA2DPCODEC
-DESCRIPTOR.enum_types_by_name['SinkState'] = _SINKSTATE
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-ApolloQATrace = _reflection.GeneratedProtocolMessageType('ApolloQATrace', (_message.Message,), dict(
-  DESCRIPTOR = _APOLLOQATRACE,
-  __module__ = 'apollo_qa_pb2'
-  # @@protoc_insertion_point(class_scope:apollo.lib.apollo_dev_util_lib.proto.ApolloQATrace)
-  ))
-_sym_db.RegisterMessage(ApolloQATrace)
-
-ApolloQAGetVerResponse = _reflection.GeneratedProtocolMessageType('ApolloQAGetVerResponse', (_message.Message,), dict(
-  DESCRIPTOR = _APOLLOQAGETVERRESPONSE,
-  __module__ = 'apollo_qa_pb2'
-  # @@protoc_insertion_point(class_scope:apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetVerResponse)
-  ))
-_sym_db.RegisterMessage(ApolloQAGetVerResponse)
-
-ApolloQAGetCodecResponse = _reflection.GeneratedProtocolMessageType('ApolloQAGetCodecResponse', (_message.Message,), dict(
-  DESCRIPTOR = _APOLLOQAGETCODECRESPONSE,
-  __module__ = 'apollo_qa_pb2'
-  # @@protoc_insertion_point(class_scope:apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetCodecResponse)
-  ))
-_sym_db.RegisterMessage(ApolloQAGetCodecResponse)
-
-ApolloQAGetDspStatusResponse = _reflection.GeneratedProtocolMessageType('ApolloQAGetDspStatusResponse', (_message.Message,), dict(
-  DESCRIPTOR = _APOLLOQAGETDSPSTATUSRESPONSE,
-  __module__ = 'apollo_qa_pb2'
-  # @@protoc_insertion_point(class_scope:apollo.lib.apollo_dev_util_lib.proto.ApolloQAGetDspStatusResponse)
-  ))
-_sym_db.RegisterMessage(ApolloQAGetDspStatusResponse)
-
-ApolloQAFactoryPlaySound = _reflection.GeneratedProtocolMessageType('ApolloQAFactoryPlaySound', (_message.Message,), dict(
-  DESCRIPTOR = _APOLLOQAFACTORYPLAYSOUND,
-  __module__ = 'apollo_qa_pb2'
-  # @@protoc_insertion_point(class_scope:apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryPlaySound)
-  ))
-_sym_db.RegisterMessage(ApolloQAFactoryPlaySound)
-
-ApolloQAFactoryInfoRequest = _reflection.GeneratedProtocolMessageType('ApolloQAFactoryInfoRequest', (_message.Message,), dict(
-  DESCRIPTOR = _APOLLOQAFACTORYINFOREQUEST,
-  __module__ = 'apollo_qa_pb2'
-  # @@protoc_insertion_point(class_scope:apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryInfoRequest)
-  ))
-_sym_db.RegisterMessage(ApolloQAFactoryInfoRequest)
-
-ApolloQAFactoryInfoResponse = _reflection.GeneratedProtocolMessageType('ApolloQAFactoryInfoResponse', (_message.Message,), dict(
-  DESCRIPTOR = _APOLLOQAFACTORYINFORESPONSE,
-  __module__ = 'apollo_qa_pb2'
-  # @@protoc_insertion_point(class_scope:apollo.lib.apollo_dev_util_lib.proto.ApolloQAFactoryInfoResponse)
-  ))
-_sym_db.RegisterMessage(ApolloQAFactoryInfoResponse)
-
-
-DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\035com.google.android.bisto.nanoB\010ApolloQA'))
-_APOLLOQATRACE.fields_by_name['data'].has_options = True
-_APOLLOQATRACE.fields_by_name['data']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\020\001\222?\002\020\005'))
-_APOLLOQAGETVERRESPONSE.fields_by_name['build_label'].has_options = True
-_APOLLOQAGETVERRESPONSE.fields_by_name['build_label']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\222?\002\010 '))
-_APOLLOQAFACTORYINFORESPONSE.fields_by_name['crystal_trim'].has_options = True
-_APOLLOQAFACTORYINFORESPONSE.fields_by_name['crystal_trim']._options = _descriptor._ParseOptions(descriptor_pb2.FieldOptions(), _b('\222?\0028\020'))
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\035com.google.android.bisto.nanoB\010ApolloQA'
+  _APOLLOQATRACE.fields_by_name['data']._options = None
+  _APOLLOQATRACE.fields_by_name['data']._serialized_options = b'\020\001\222?\002\020\005'
+  _APOLLOQAGETVERRESPONSE.fields_by_name['build_label']._options = None
+  _APOLLOQAGETVERRESPONSE.fields_by_name['build_label']._serialized_options = b'\222?\002\010 '
+  _APOLLOQAFACTORYINFORESPONSE.fields_by_name['crystal_trim']._options = None
+  _APOLLOQAFACTORYINFORESPONSE.fields_by_name['crystal_trim']._serialized_options = b'\222?\0028\020'
+  _APOLLOQAMESSAGETYPE._serialized_start=1217
+  _APOLLOQAMESSAGETYPE._serialized_end=1401
+  _TRACEID._serialized_start=1404
+  _TRACEID._serialized_end=1784
+  _AVRCPPLAYSTATUS._serialized_start=1786
+  _AVRCPPLAYSTATUS._serialized_end=1895
+  _PREVIOUSBOOTSTATUS._serialized_start=1897
+  _PREVIOUSBOOTSTATUS._serialized_end=1949
+  _APOLLOQAA2DPCODEC._serialized_start=1951
+  _APOLLOQAA2DPCODEC._serialized_end=1988
+  _SINKSTATE._serialized_start=1991
+  _SINKSTATE._serialized_end=2335
+  _APOLLOQATRACE._serialized_start=71
+  _APOLLOQATRACE._serialized_end=187
+  _APOLLOQAGETVERRESPONSE._serialized_start=190
+  _APOLLOQAGETVERRESPONSE._serialized_end=523
+  _APOLLOQAGETCODECRESPONSE._serialized_start=525
+  _APOLLOQAGETCODECRESPONSE._serialized_end=642
+  _APOLLOQAGETDSPSTATUSRESPONSE._serialized_start=645
+  _APOLLOQAGETDSPSTATUSRESPONSE._serialized_end=811
+  _APOLLOQAFACTORYPLAYSOUND._serialized_start=814
+  _APOLLOQAFACTORYPLAYSOUND._serialized_end=999
+  _APOLLOQAFACTORYPLAYSOUND_PROMPTTYPE._serialized_start=933
+  _APOLLOQAFACTORYPLAYSOUND_PROMPTTYPE._serialized_end=999
+  _APOLLOQAFACTORYINFOREQUEST._serialized_start=1001
+  _APOLLOQAFACTORYINFOREQUEST._serialized_end=1029
+  _APOLLOQAFACTORYINFORESPONSE._serialized_start=1032
+  _APOLLOQAFACTORYINFORESPONSE._serialized_end=1214
 # @@protoc_insertion_point(module_scope)
diff --git a/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/audiowear_pb2.py b/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/audiowear_pb2.py
index 094a868..123a079 100644
--- a/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/audiowear_pb2.py
+++ b/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/audiowear_pb2.py
@@ -1,14 +1,11 @@
+# -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: audiowear.proto
-
-import sys
-_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
-from google.protobuf.internal import enum_type_wrapper
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
-from google.protobuf import descriptor_pb2
 # @@protoc_insertion_point(imports)
 
 _sym_db = _symbol_database.Default()
@@ -16,109 +13,14 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='audiowear.proto',
-  package='apollo.lib.apollo_dev_util_lib.proto',
-  syntax='proto2',
-  serialized_pb=_b('\n\x0f\x61udiowear.proto\x12$apollo.lib.apollo_dev_util_lib.proto*\x8d\x02\n\x0cMessageGroup\x12\x19\n\x15UNKNOWN_MESSAGE_GROUP\x10\x00\x12\x10\n\x0c\x44\x45VICE_INPUT\x10\x01\x12\x07\n\x03OTA\x10\x02\x12\x15\n\x11\x44\x45VICE_CAPABILITY\x10\x03\x12\x11\n\rDEVICE_STATUS\x10\x04\x12\x0b\n\x07LOGGING\x10\x05\x12\x0b\n\x07SENSORS\x10\x06\x12\x14\n\x10\x43OMPANION_STATUS\x10\x07\x12\x12\n\x0e\x44\x45VICE_COMMAND\x10\x08\x12\x12\n\x0e\x42ISTO_SETTINGS\x10\t\x12\x0c\n\x08WELLNESS\x10\n\x12\x08\n\x04TEST\x10\x0b\x12\x0f\n\x0b\x42LE_SERVICE\x10\x0c\x12\r\n\tAPOLLO_QA\x10~\x12\r\n\tTRANSLATE\x10\x7f\x42)\n\x1d\x63om.google.android.bisto.nanoB\x08Protocol')
-)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0f\x61udiowear.proto\x12$apollo.lib.apollo_dev_util_lib.proto*\x8d\x02\n\x0cMessageGroup\x12\x19\n\x15UNKNOWN_MESSAGE_GROUP\x10\x00\x12\x10\n\x0c\x44\x45VICE_INPUT\x10\x01\x12\x07\n\x03OTA\x10\x02\x12\x15\n\x11\x44\x45VICE_CAPABILITY\x10\x03\x12\x11\n\rDEVICE_STATUS\x10\x04\x12\x0b\n\x07LOGGING\x10\x05\x12\x0b\n\x07SENSORS\x10\x06\x12\x14\n\x10\x43OMPANION_STATUS\x10\x07\x12\x12\n\x0e\x44\x45VICE_COMMAND\x10\x08\x12\x12\n\x0e\x42ISTO_SETTINGS\x10\t\x12\x0c\n\x08WELLNESS\x10\n\x12\x08\n\x04TEST\x10\x0b\x12\x0f\n\x0b\x42LE_SERVICE\x10\x0c\x12\r\n\tAPOLLO_QA\x10~\x12\r\n\tTRANSLATE\x10\x7f\x42)\n\x1d\x63om.google.android.bisto.nanoB\x08Protocol')
 
-_MESSAGEGROUP = _descriptor.EnumDescriptor(
-  name='MessageGroup',
-  full_name='apollo.lib.apollo_dev_util_lib.proto.MessageGroup',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='UNKNOWN_MESSAGE_GROUP', index=0, number=0,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DEVICE_INPUT', index=1, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='OTA', index=2, number=2,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DEVICE_CAPABILITY', index=3, number=3,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DEVICE_STATUS', index=4, number=4,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='LOGGING', index=5, number=5,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='SENSORS', index=6, number=6,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='COMPANION_STATUS', index=7, number=7,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='DEVICE_COMMAND', index=8, number=8,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='BISTO_SETTINGS', index=9, number=9,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='WELLNESS', index=10, number=10,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TEST', index=11, number=11,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='BLE_SERVICE', index=12, number=12,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='APOLLO_QA', index=13, number=126,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TRANSLATE', index=14, number=127,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=58,
-  serialized_end=327,
-)
-_sym_db.RegisterEnumDescriptor(_MESSAGEGROUP)
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'audiowear_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-MessageGroup = enum_type_wrapper.EnumTypeWrapper(_MESSAGEGROUP)
-UNKNOWN_MESSAGE_GROUP = 0
-DEVICE_INPUT = 1
-OTA = 2
-DEVICE_CAPABILITY = 3
-DEVICE_STATUS = 4
-LOGGING = 5
-SENSORS = 6
-COMPANION_STATUS = 7
-DEVICE_COMMAND = 8
-BISTO_SETTINGS = 9
-WELLNESS = 10
-TEST = 11
-BLE_SERVICE = 12
-APOLLO_QA = 126
-TRANSLATE = 127
-
-
-DESCRIPTOR.enum_types_by_name['MessageGroup'] = _MESSAGEGROUP
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-
-DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\035com.google.android.bisto.nanoB\010Protocol'))
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\035com.google.android.bisto.nanoB\010Protocol'
+  _MESSAGEGROUP._serialized_start=58
+  _MESSAGEGROUP._serialized_end=327
 # @@protoc_insertion_point(module_scope)
diff --git a/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/google/protobuf/descriptor_pb2.py b/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/google/protobuf/descriptor_pb2.py
index e3b4558..dd9775c 100644
--- a/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/google/protobuf/descriptor_pb2.py
+++ b/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/google/protobuf/descriptor_pb2.py
@@ -1,11 +1,10 @@
+# -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: google/protobuf/descriptor.proto
-
-import sys
-_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -14,1559 +13,1470 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='google/protobuf/descriptor.proto',
-  package='google.protobuf',
-  syntax='proto2',
-  serialized_pb=_b('\n google/protobuf/descriptor.proto\x12\x0fgoogle.protobuf\"G\n\x11\x46ileDescriptorSet\x12\x32\n\x04\x66ile\x18\x01 \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\"\xdb\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12\x36\n\x0cmessage_type\x18\x04 \x03(\x0b\x32 .google.protobuf.DescriptorProto\x12\x37\n\tenum_type\x18\x05 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProto\x12\x38\n\x07service\x18\x06 \x03(\x0b\x32\'.google.protobuf.ServiceDescriptorProto\x12\x38\n\textension\x18\x07 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12-\n\x07options\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.FileOptions\x12\x39\n\x10source_code_info\x18\t \x01(\x0b\x32\x1f.google.protobuf.SourceCodeInfo\x12\x0e\n\x06syntax\x18\x0c \x01(\t\"\xe4\x03\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x34\n\x05\x66ield\x18\x02 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12\x38\n\textension\x18\x06 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12\x35\n\x0bnested_type\x18\x03 \x03(\x0b\x32 .google.protobuf.DescriptorProto\x12\x37\n\tenum_type\x18\x04 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProto\x12H\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32/.google.protobuf.DescriptorProto.ExtensionRange\x12\x39\n\noneof_decl\x18\x08 \x03(\x0b\x32%.google.protobuf.OneofDescriptorProto\x12\x30\n\x07options\x18\x07 \x01(\x0b\x32\x1f.google.protobuf.MessageOptions\x1a,\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"\xa9\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12:\n\x05label\x18\x04 \x01(\x0e\x32+.google.protobuf.FieldDescriptorProto.Label\x12\x38\n\x04type\x18\x05 \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12.\n\x07options\x18\x08 \x01(\x0b\x32\x1d.google.protobuf.FieldOptions\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"$\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x8c\x01\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x38\n\x05value\x18\x02 \x03(\x0b\x32).google.protobuf.EnumValueDescriptorProto\x12-\n\x07options\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.EnumOptions\"l\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12\x32\n\x07options\x18\x03 \x01(\x0b\x32!.google.protobuf.EnumValueOptions\"\x90\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x36\n\x06method\x18\x02 \x03(\x0b\x32&.google.protobuf.MethodDescriptorProto\x12\x30\n\x07options\x18\x03 \x01(\x0b\x32\x1f.google.protobuf.ServiceOptions\"\xc1\x01\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12/\n\x07options\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.MethodOptions\x12\x1f\n\x10\x63lient_streaming\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10server_streaming\x18\x06 \x01(\x08:\x05\x66\x61lse\"\xcc\x04\n\x0b\x46ileOptions\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12,\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08:\x05\x66\x61lse\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12\x46\n\x0coptimize_for\x18\t \x01(\x0e\x32).google.protobuf.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10\x63\x63_enable_arenas\x18\x1f \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xe6\x01\n\x0eMessageOptions\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x11\n\tmap_entry\x18\x07 \x01(\x08\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xa0\x02\n\x0c\x46ieldOptions\x12:\n\x05\x63type\x18\x01 \x01(\x0e\x32#.google.protobuf.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x8d\x01\n\x0b\x45numOptions\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"}\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"{\n\x0eServiceOptions\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"z\n\rMethodOptions\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9e\x02\n\x13UninterpretedOption\x12;\n\x04name\x18\x02 \x03(\x0b\x32-.google.protobuf.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xb1\x01\n\x0eSourceCodeInfo\x12:\n\x08location\x18\x01 \x03(\x0b\x32(.google.protobuf.SourceCodeInfo.Location\x1a\x63\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\tB)\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01')
-)
+if _descriptor._USE_C_DESCRIPTORS == False:
+  DESCRIPTOR = _descriptor.FileDescriptor(
+    name='google/protobuf/descriptor.proto',
+    package='google.protobuf',
+    syntax='proto2',
+    serialized_options=None,
+    create_key=_descriptor._internal_create_key,
+    serialized_pb=b'\n google/protobuf/descriptor.proto\x12\x0fgoogle.protobuf\"G\n\x11\x46ileDescriptorSet\x12\x32\n\x04\x66ile\x18\x01 \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\"\xdb\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12\x36\n\x0cmessage_type\x18\x04 \x03(\x0b\x32 .google.protobuf.DescriptorProto\x12\x37\n\tenum_type\x18\x05 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProto\x12\x38\n\x07service\x18\x06 \x03(\x0b\x32\'.google.protobuf.ServiceDescriptorProto\x12\x38\n\textension\x18\x07 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12-\n\x07options\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.FileOptions\x12\x39\n\x10source_code_info\x18\t \x01(\x0b\x32\x1f.google.protobuf.SourceCodeInfo\x12\x0e\n\x06syntax\x18\x0c \x01(\t\"\xe4\x03\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x34\n\x05\x66ield\x18\x02 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12\x38\n\textension\x18\x06 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12\x35\n\x0bnested_type\x18\x03 \x03(\x0b\x32 .google.protobuf.DescriptorProto\x12\x37\n\tenum_type\x18\x04 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProto\x12H\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32/.google.protobuf.DescriptorProto.ExtensionRange\x12\x39\n\noneof_decl\x18\x08 \x03(\x0b\x32%.google.protobuf.OneofDescriptorProto\x12\x30\n\x07options\x18\x07 \x01(\x0b\x32\x1f.google.protobuf.MessageOptions\x1a,\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"\xa9\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12:\n\x05label\x18\x04 \x01(\x0e\x32+.google.protobuf.FieldDescriptorProto.Label\x12\x38\n\x04type\x18\x05 \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12.\n\x07options\x18\x08 \x01(\x0b\x32\x1d.google.protobuf.FieldOptions\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"$\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x8c\x01\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x38\n\x05value\x18\x02 \x03(\x0b\x32).google.protobuf.EnumValueDescriptorProto\x12-\n\x07options\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.EnumOptions\"l\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12\x32\n\x07options\x18\x03 \x01(\x0b\x32!.google.protobuf.EnumValueOptions\"\x90\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x36\n\x06method\x18\x02 \x03(\x0b\x32&.google.protobuf.MethodDescriptorProto\x12\x30\n\x07options\x18\x03 \x01(\x0b\x32\x1f.google.protobuf.ServiceOptions\"\xc1\x01\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12/\n\x07options\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.MethodOptions\x12\x1f\n\x10\x63lient_streaming\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10server_streaming\x18\x06 \x01(\x08:\x05\x66\x61lse\"\xcc\x04\n\x0b\x46ileOptions\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12,\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08:\x05\x66\x61lse\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12\x46\n\x0coptimize_for\x18\t \x01(\x0e\x32).google.protobuf.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10\x63\x63_enable_arenas\x18\x1f \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xe6\x01\n\x0eMessageOptions\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x11\n\tmap_entry\x18\x07 \x01(\x08\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xa0\x02\n\x0c\x46ieldOptions\x12:\n\x05\x63type\x18\x01 \x01(\x0e\x32#.google.protobuf.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x8d\x01\n\x0b\x45numOptions\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"}\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"{\n\x0eServiceOptions\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"z\n\rMethodOptions\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9e\x02\n\x13UninterpretedOption\x12;\n\x04name\x18\x02 \x03(\x0b\x32-.google.protobuf.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xb1\x01\n\x0eSourceCodeInfo\x12:\n\x08location\x18\x01 \x03(\x0b\x32(.google.protobuf.SourceCodeInfo.Location\x1a\x63\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\tB)\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01'
+  )
+else:
+  DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n google/protobuf/descriptor.proto\x12\x0fgoogle.protobuf\"G\n\x11\x46ileDescriptorSet\x12\x32\n\x04\x66ile\x18\x01 \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\"\xdb\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12\x36\n\x0cmessage_type\x18\x04 \x03(\x0b\x32 .google.protobuf.DescriptorProto\x12\x37\n\tenum_type\x18\x05 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProto\x12\x38\n\x07service\x18\x06 \x03(\x0b\x32\'.google.protobuf.ServiceDescriptorProto\x12\x38\n\textension\x18\x07 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12-\n\x07options\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.FileOptions\x12\x39\n\x10source_code_info\x18\t \x01(\x0b\x32\x1f.google.protobuf.SourceCodeInfo\x12\x0e\n\x06syntax\x18\x0c \x01(\t\"\xe4\x03\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x34\n\x05\x66ield\x18\x02 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12\x38\n\textension\x18\x06 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12\x35\n\x0bnested_type\x18\x03 \x03(\x0b\x32 .google.protobuf.DescriptorProto\x12\x37\n\tenum_type\x18\x04 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProto\x12H\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32/.google.protobuf.DescriptorProto.ExtensionRange\x12\x39\n\noneof_decl\x18\x08 \x03(\x0b\x32%.google.protobuf.OneofDescriptorProto\x12\x30\n\x07options\x18\x07 \x01(\x0b\x32\x1f.google.protobuf.MessageOptions\x1a,\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"\xa9\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12:\n\x05label\x18\x04 \x01(\x0e\x32+.google.protobuf.FieldDescriptorProto.Label\x12\x38\n\x04type\x18\x05 \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12.\n\x07options\x18\x08 \x01(\x0b\x32\x1d.google.protobuf.FieldOptions\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"$\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x8c\x01\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x38\n\x05value\x18\x02 \x03(\x0b\x32).google.protobuf.EnumValueDescriptorProto\x12-\n\x07options\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.EnumOptions\"l\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12\x32\n\x07options\x18\x03 \x01(\x0b\x32!.google.protobuf.EnumValueOptions\"\x90\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x36\n\x06method\x18\x02 \x03(\x0b\x32&.google.protobuf.MethodDescriptorProto\x12\x30\n\x07options\x18\x03 \x01(\x0b\x32\x1f.google.protobuf.ServiceOptions\"\xc1\x01\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12/\n\x07options\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.MethodOptions\x12\x1f\n\x10\x63lient_streaming\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10server_streaming\x18\x06 \x01(\x08:\x05\x66\x61lse\"\xcc\x04\n\x0b\x46ileOptions\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12,\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08:\x05\x66\x61lse\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12\x46\n\x0coptimize_for\x18\t \x01(\x0e\x32).google.protobuf.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10\x63\x63_enable_arenas\x18\x1f \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xe6\x01\n\x0eMessageOptions\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x11\n\tmap_entry\x18\x07 \x01(\x08\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xa0\x02\n\x0c\x46ieldOptions\x12:\n\x05\x63type\x18\x01 \x01(\x0e\x32#.google.protobuf.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x8d\x01\n\x0b\x45numOptions\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"}\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"{\n\x0eServiceOptions\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"z\n\rMethodOptions\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9e\x02\n\x13UninterpretedOption\x12;\n\x04name\x18\x02 \x03(\x0b\x32-.google.protobuf.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xb1\x01\n\x0eSourceCodeInfo\x12:\n\x08location\x18\x01 \x03(\x0b\x32(.google.protobuf.SourceCodeInfo.Location\x1a\x63\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\tB)\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01')
 
+if _descriptor._USE_C_DESCRIPTORS == False:
+  _FIELDDESCRIPTORPROTO_TYPE = _descriptor.EnumDescriptor(
+    name='Type',
+    full_name='google.protobuf.FieldDescriptorProto.Type',
+    filename=None,
+    file=DESCRIPTOR,
+    create_key=_descriptor._internal_create_key,
+    values=[
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_DOUBLE', index=0, number=1,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_FLOAT', index=1, number=2,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_INT64', index=2, number=3,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_UINT64', index=3, number=4,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_INT32', index=4, number=5,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_FIXED64', index=5, number=6,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_FIXED32', index=6, number=7,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_BOOL', index=7, number=8,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_STRING', index=8, number=9,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_GROUP', index=9, number=10,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_MESSAGE', index=10, number=11,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_BYTES', index=11, number=12,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_UINT32', index=12, number=13,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_ENUM', index=13, number=14,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_SFIXED32', index=14, number=15,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_SFIXED64', index=15, number=16,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_SINT32', index=16, number=17,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='TYPE_SINT64', index=17, number=18,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+    ],
+    containing_type=None,
+    serialized_options=None,
+  )
+  _sym_db.RegisterEnumDescriptor(_FIELDDESCRIPTORPROTO_TYPE)
 
+  _FIELDDESCRIPTORPROTO_LABEL = _descriptor.EnumDescriptor(
+    name='Label',
+    full_name='google.protobuf.FieldDescriptorProto.Label',
+    filename=None,
+    file=DESCRIPTOR,
+    create_key=_descriptor._internal_create_key,
+    values=[
+      _descriptor.EnumValueDescriptor(
+        name='LABEL_OPTIONAL', index=0, number=1,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='LABEL_REQUIRED', index=1, number=2,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='LABEL_REPEATED', index=2, number=3,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+    ],
+    containing_type=None,
+    serialized_options=None,
+  )
+  _sym_db.RegisterEnumDescriptor(_FIELDDESCRIPTORPROTO_LABEL)
 
-_FIELDDESCRIPTORPROTO_TYPE = _descriptor.EnumDescriptor(
-  name='Type',
-  full_name='google.protobuf.FieldDescriptorProto.Type',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_DOUBLE', index=0, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_FLOAT', index=1, number=2,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_INT64', index=2, number=3,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_UINT64', index=3, number=4,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_INT32', index=4, number=5,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_FIXED64', index=5, number=6,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_FIXED32', index=6, number=7,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_BOOL', index=7, number=8,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_STRING', index=8, number=9,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_GROUP', index=9, number=10,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_MESSAGE', index=10, number=11,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_BYTES', index=11, number=12,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_UINT32', index=12, number=13,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_ENUM', index=13, number=14,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_SFIXED32', index=14, number=15,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_SFIXED64', index=15, number=16,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_SINT32', index=16, number=17,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TYPE_SINT64', index=17, number=18,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=1394,
-  serialized_end=1704,
-)
-_sym_db.RegisterEnumDescriptor(_FIELDDESCRIPTORPROTO_TYPE)
+  _FILEOPTIONS_OPTIMIZEMODE = _descriptor.EnumDescriptor(
+    name='OptimizeMode',
+    full_name='google.protobuf.FileOptions.OptimizeMode',
+    filename=None,
+    file=DESCRIPTOR,
+    create_key=_descriptor._internal_create_key,
+    values=[
+      _descriptor.EnumValueDescriptor(
+        name='SPEED', index=0, number=1,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='CODE_SIZE', index=1, number=2,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='LITE_RUNTIME', index=2, number=3,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+    ],
+    containing_type=None,
+    serialized_options=None,
+  )
+  _sym_db.RegisterEnumDescriptor(_FILEOPTIONS_OPTIMIZEMODE)
 
-_FIELDDESCRIPTORPROTO_LABEL = _descriptor.EnumDescriptor(
-  name='Label',
-  full_name='google.protobuf.FieldDescriptorProto.Label',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='LABEL_OPTIONAL', index=0, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='LABEL_REQUIRED', index=1, number=2,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='LABEL_REPEATED', index=2, number=3,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=1706,
-  serialized_end=1773,
-)
-_sym_db.RegisterEnumDescriptor(_FIELDDESCRIPTORPROTO_LABEL)
+  _FIELDOPTIONS_CTYPE = _descriptor.EnumDescriptor(
+    name='CType',
+    full_name='google.protobuf.FieldOptions.CType',
+    filename=None,
+    file=DESCRIPTOR,
+    create_key=_descriptor._internal_create_key,
+    values=[
+      _descriptor.EnumValueDescriptor(
+        name='STRING', index=0, number=0,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='CORD', index=1, number=1,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+      _descriptor.EnumValueDescriptor(
+        name='STRING_PIECE', index=2, number=2,
+        serialized_options=None,
+        type=None,
+        create_key=_descriptor._internal_create_key),
+    ],
+    containing_type=None,
+    serialized_options=None,
+  )
+  _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_CTYPE)
 
-_FILEOPTIONS_OPTIMIZEMODE = _descriptor.EnumDescriptor(
-  name='OptimizeMode',
-  full_name='google.protobuf.FileOptions.OptimizeMode',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='SPEED', index=0, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='CODE_SIZE', index=1, number=2,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='LITE_RUNTIME', index=2, number=3,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=2929,
-  serialized_end=2987,
-)
-_sym_db.RegisterEnumDescriptor(_FILEOPTIONS_OPTIMIZEMODE)
 
-_FIELDOPTIONS_CTYPE = _descriptor.EnumDescriptor(
-  name='CType',
-  full_name='google.protobuf.FieldOptions.CType',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='STRING', index=0, number=0,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='CORD', index=1, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='STRING_PIECE', index=2, number=2,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=3464,
-  serialized_end=3511,
-)
-_sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_CTYPE)
+  _FILEDESCRIPTORSET = _descriptor.Descriptor(
+    name='FileDescriptorSet',
+    full_name='google.protobuf.FileDescriptorSet',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='file', full_name='google.protobuf.FileDescriptorSet.file', index=0,
+        number=1, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
 
-_FILEDESCRIPTORSET = _descriptor.Descriptor(
-  name='FileDescriptorSet',
-  full_name='google.protobuf.FileDescriptorSet',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='file', full_name='google.protobuf.FileDescriptorSet.file', index=0,
-      number=1, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=53,
-  serialized_end=124,
-)
+  _FILEDESCRIPTORPROTO = _descriptor.Descriptor(
+    name='FileDescriptorProto',
+    full_name='google.protobuf.FileDescriptorProto',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='name', full_name='google.protobuf.FileDescriptorProto.name', index=0,
+        number=1, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='package', full_name='google.protobuf.FileDescriptorProto.package', index=1,
+        number=2, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='dependency', full_name='google.protobuf.FileDescriptorProto.dependency', index=2,
+        number=3, type=9, cpp_type=9, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='public_dependency', full_name='google.protobuf.FileDescriptorProto.public_dependency', index=3,
+        number=10, type=5, cpp_type=1, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='weak_dependency', full_name='google.protobuf.FileDescriptorProto.weak_dependency', index=4,
+        number=11, type=5, cpp_type=1, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='message_type', full_name='google.protobuf.FileDescriptorProto.message_type', index=5,
+        number=4, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='enum_type', full_name='google.protobuf.FileDescriptorProto.enum_type', index=6,
+        number=5, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='service', full_name='google.protobuf.FileDescriptorProto.service', index=7,
+        number=6, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='extension', full_name='google.protobuf.FileDescriptorProto.extension', index=8,
+        number=7, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='options', full_name='google.protobuf.FileDescriptorProto.options', index=9,
+        number=8, type=11, cpp_type=10, label=1,
+        has_default_value=False, default_value=None,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='source_code_info', full_name='google.protobuf.FileDescriptorProto.source_code_info', index=10,
+        number=9, type=11, cpp_type=10, label=1,
+        has_default_value=False, default_value=None,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='syntax', full_name='google.protobuf.FileDescriptorProto.syntax', index=11,
+        number=12, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
 
-_FILEDESCRIPTORPROTO = _descriptor.Descriptor(
-  name='FileDescriptorProto',
-  full_name='google.protobuf.FileDescriptorProto',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='google.protobuf.FileDescriptorProto.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='package', full_name='google.protobuf.FileDescriptorProto.package', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='dependency', full_name='google.protobuf.FileDescriptorProto.dependency', index=2,
-      number=3, type=9, cpp_type=9, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='public_dependency', full_name='google.protobuf.FileDescriptorProto.public_dependency', index=3,
-      number=10, type=5, cpp_type=1, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='weak_dependency', full_name='google.protobuf.FileDescriptorProto.weak_dependency', index=4,
-      number=11, type=5, cpp_type=1, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='message_type', full_name='google.protobuf.FileDescriptorProto.message_type', index=5,
-      number=4, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='enum_type', full_name='google.protobuf.FileDescriptorProto.enum_type', index=6,
-      number=5, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='service', full_name='google.protobuf.FileDescriptorProto.service', index=7,
-      number=6, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='extension', full_name='google.protobuf.FileDescriptorProto.extension', index=8,
-      number=7, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='options', full_name='google.protobuf.FileDescriptorProto.options', index=9,
-      number=8, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='source_code_info', full_name='google.protobuf.FileDescriptorProto.source_code_info', index=10,
-      number=9, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='syntax', full_name='google.protobuf.FileDescriptorProto.syntax', index=11,
-      number=12, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=127,
-  serialized_end=602,
-)
+  _DESCRIPTORPROTO_EXTENSIONRANGE = _descriptor.Descriptor(
+    name='ExtensionRange',
+    full_name='google.protobuf.DescriptorProto.ExtensionRange',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='start', full_name='google.protobuf.DescriptorProto.ExtensionRange.start', index=0,
+        number=1, type=5, cpp_type=1, label=1,
+        has_default_value=False, default_value=0,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='end', full_name='google.protobuf.DescriptorProto.ExtensionRange.end', index=1,
+        number=2, type=5, cpp_type=1, label=1,
+        has_default_value=False, default_value=0,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
+  _DESCRIPTORPROTO = _descriptor.Descriptor(
+    name='DescriptorProto',
+    full_name='google.protobuf.DescriptorProto',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='name', full_name='google.protobuf.DescriptorProto.name', index=0,
+        number=1, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='field', full_name='google.protobuf.DescriptorProto.field', index=1,
+        number=2, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='extension', full_name='google.protobuf.DescriptorProto.extension', index=2,
+        number=6, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='nested_type', full_name='google.protobuf.DescriptorProto.nested_type', index=3,
+        number=3, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='enum_type', full_name='google.protobuf.DescriptorProto.enum_type', index=4,
+        number=4, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='extension_range', full_name='google.protobuf.DescriptorProto.extension_range', index=5,
+        number=5, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='oneof_decl', full_name='google.protobuf.DescriptorProto.oneof_decl', index=6,
+        number=8, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='options', full_name='google.protobuf.DescriptorProto.options', index=7,
+        number=7, type=11, cpp_type=10, label=1,
+        has_default_value=False, default_value=None,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[_DESCRIPTORPROTO_EXTENSIONRANGE, ],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
-_DESCRIPTORPROTO_EXTENSIONRANGE = _descriptor.Descriptor(
-  name='ExtensionRange',
-  full_name='google.protobuf.DescriptorProto.ExtensionRange',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='start', full_name='google.protobuf.DescriptorProto.ExtensionRange.start', index=0,
-      number=1, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='end', full_name='google.protobuf.DescriptorProto.ExtensionRange.end', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1045,
-  serialized_end=1089,
-)
 
-_DESCRIPTORPROTO = _descriptor.Descriptor(
-  name='DescriptorProto',
-  full_name='google.protobuf.DescriptorProto',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='google.protobuf.DescriptorProto.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='field', full_name='google.protobuf.DescriptorProto.field', index=1,
-      number=2, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='extension', full_name='google.protobuf.DescriptorProto.extension', index=2,
-      number=6, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='nested_type', full_name='google.protobuf.DescriptorProto.nested_type', index=3,
-      number=3, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='enum_type', full_name='google.protobuf.DescriptorProto.enum_type', index=4,
-      number=4, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='extension_range', full_name='google.protobuf.DescriptorProto.extension_range', index=5,
-      number=5, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='oneof_decl', full_name='google.protobuf.DescriptorProto.oneof_decl', index=6,
-      number=8, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='options', full_name='google.protobuf.DescriptorProto.options', index=7,
-      number=7, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[_DESCRIPTORPROTO_EXTENSIONRANGE, ],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=605,
-  serialized_end=1089,
-)
+  _FIELDDESCRIPTORPROTO = _descriptor.Descriptor(
+    name='FieldDescriptorProto',
+    full_name='google.protobuf.FieldDescriptorProto',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='name', full_name='google.protobuf.FieldDescriptorProto.name', index=0,
+        number=1, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='number', full_name='google.protobuf.FieldDescriptorProto.number', index=1,
+        number=3, type=5, cpp_type=1, label=1,
+        has_default_value=False, default_value=0,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='label', full_name='google.protobuf.FieldDescriptorProto.label', index=2,
+        number=4, type=14, cpp_type=8, label=1,
+        has_default_value=False, default_value=1,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='type', full_name='google.protobuf.FieldDescriptorProto.type', index=3,
+        number=5, type=14, cpp_type=8, label=1,
+        has_default_value=False, default_value=1,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='type_name', full_name='google.protobuf.FieldDescriptorProto.type_name', index=4,
+        number=6, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='extendee', full_name='google.protobuf.FieldDescriptorProto.extendee', index=5,
+        number=2, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='default_value', full_name='google.protobuf.FieldDescriptorProto.default_value', index=6,
+        number=7, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='oneof_index', full_name='google.protobuf.FieldDescriptorProto.oneof_index', index=7,
+        number=9, type=5, cpp_type=1, label=1,
+        has_default_value=False, default_value=0,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='options', full_name='google.protobuf.FieldDescriptorProto.options', index=8,
+        number=8, type=11, cpp_type=10, label=1,
+        has_default_value=False, default_value=None,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+      _FIELDDESCRIPTORPROTO_TYPE,
+      _FIELDDESCRIPTORPROTO_LABEL,
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
 
-_FIELDDESCRIPTORPROTO = _descriptor.Descriptor(
-  name='FieldDescriptorProto',
-  full_name='google.protobuf.FieldDescriptorProto',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='google.protobuf.FieldDescriptorProto.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='number', full_name='google.protobuf.FieldDescriptorProto.number', index=1,
-      number=3, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='label', full_name='google.protobuf.FieldDescriptorProto.label', index=2,
-      number=4, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=1,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='type', full_name='google.protobuf.FieldDescriptorProto.type', index=3,
-      number=5, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=1,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='type_name', full_name='google.protobuf.FieldDescriptorProto.type_name', index=4,
-      number=6, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='extendee', full_name='google.protobuf.FieldDescriptorProto.extendee', index=5,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='default_value', full_name='google.protobuf.FieldDescriptorProto.default_value', index=6,
-      number=7, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='oneof_index', full_name='google.protobuf.FieldDescriptorProto.oneof_index', index=7,
-      number=9, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='options', full_name='google.protobuf.FieldDescriptorProto.options', index=8,
-      number=8, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _FIELDDESCRIPTORPROTO_TYPE,
-    _FIELDDESCRIPTORPROTO_LABEL,
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1092,
-  serialized_end=1773,
-)
+  _ONEOFDESCRIPTORPROTO = _descriptor.Descriptor(
+    name='OneofDescriptorProto',
+    full_name='google.protobuf.OneofDescriptorProto',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='name', full_name='google.protobuf.OneofDescriptorProto.name', index=0,
+        number=1, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
 
-_ONEOFDESCRIPTORPROTO = _descriptor.Descriptor(
-  name='OneofDescriptorProto',
-  full_name='google.protobuf.OneofDescriptorProto',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='google.protobuf.OneofDescriptorProto.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1775,
-  serialized_end=1811,
-)
+  _ENUMDESCRIPTORPROTO = _descriptor.Descriptor(
+    name='EnumDescriptorProto',
+    full_name='google.protobuf.EnumDescriptorProto',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='name', full_name='google.protobuf.EnumDescriptorProto.name', index=0,
+        number=1, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='value', full_name='google.protobuf.EnumDescriptorProto.value', index=1,
+        number=2, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='options', full_name='google.protobuf.EnumDescriptorProto.options', index=2,
+        number=3, type=11, cpp_type=10, label=1,
+        has_default_value=False, default_value=None,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
 
-_ENUMDESCRIPTORPROTO = _descriptor.Descriptor(
-  name='EnumDescriptorProto',
-  full_name='google.protobuf.EnumDescriptorProto',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='google.protobuf.EnumDescriptorProto.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='value', full_name='google.protobuf.EnumDescriptorProto.value', index=1,
-      number=2, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='options', full_name='google.protobuf.EnumDescriptorProto.options', index=2,
-      number=3, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1814,
-  serialized_end=1954,
-)
+  _ENUMVALUEDESCRIPTORPROTO = _descriptor.Descriptor(
+    name='EnumValueDescriptorProto',
+    full_name='google.protobuf.EnumValueDescriptorProto',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='name', full_name='google.protobuf.EnumValueDescriptorProto.name', index=0,
+        number=1, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='number', full_name='google.protobuf.EnumValueDescriptorProto.number', index=1,
+        number=2, type=5, cpp_type=1, label=1,
+        has_default_value=False, default_value=0,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='options', full_name='google.protobuf.EnumValueDescriptorProto.options', index=2,
+        number=3, type=11, cpp_type=10, label=1,
+        has_default_value=False, default_value=None,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
 
-_ENUMVALUEDESCRIPTORPROTO = _descriptor.Descriptor(
-  name='EnumValueDescriptorProto',
-  full_name='google.protobuf.EnumValueDescriptorProto',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='google.protobuf.EnumValueDescriptorProto.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='number', full_name='google.protobuf.EnumValueDescriptorProto.number', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='options', full_name='google.protobuf.EnumValueDescriptorProto.options', index=2,
-      number=3, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1956,
-  serialized_end=2064,
-)
+  _SERVICEDESCRIPTORPROTO = _descriptor.Descriptor(
+    name='ServiceDescriptorProto',
+    full_name='google.protobuf.ServiceDescriptorProto',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='name', full_name='google.protobuf.ServiceDescriptorProto.name', index=0,
+        number=1, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='method', full_name='google.protobuf.ServiceDescriptorProto.method', index=1,
+        number=2, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='options', full_name='google.protobuf.ServiceDescriptorProto.options', index=2,
+        number=3, type=11, cpp_type=10, label=1,
+        has_default_value=False, default_value=None,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
 
-_SERVICEDESCRIPTORPROTO = _descriptor.Descriptor(
-  name='ServiceDescriptorProto',
-  full_name='google.protobuf.ServiceDescriptorProto',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='google.protobuf.ServiceDescriptorProto.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='method', full_name='google.protobuf.ServiceDescriptorProto.method', index=1,
-      number=2, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='options', full_name='google.protobuf.ServiceDescriptorProto.options', index=2,
-      number=3, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2067,
-  serialized_end=2211,
-)
+  _METHODDESCRIPTORPROTO = _descriptor.Descriptor(
+    name='MethodDescriptorProto',
+    full_name='google.protobuf.MethodDescriptorProto',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='name', full_name='google.protobuf.MethodDescriptorProto.name', index=0,
+        number=1, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='input_type', full_name='google.protobuf.MethodDescriptorProto.input_type', index=1,
+        number=2, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='output_type', full_name='google.protobuf.MethodDescriptorProto.output_type', index=2,
+        number=3, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='options', full_name='google.protobuf.MethodDescriptorProto.options', index=3,
+        number=4, type=11, cpp_type=10, label=1,
+        has_default_value=False, default_value=None,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='client_streaming', full_name='google.protobuf.MethodDescriptorProto.client_streaming', index=4,
+        number=5, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='server_streaming', full_name='google.protobuf.MethodDescriptorProto.server_streaming', index=5,
+        number=6, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
 
-_METHODDESCRIPTORPROTO = _descriptor.Descriptor(
-  name='MethodDescriptorProto',
-  full_name='google.protobuf.MethodDescriptorProto',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='google.protobuf.MethodDescriptorProto.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='input_type', full_name='google.protobuf.MethodDescriptorProto.input_type', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='output_type', full_name='google.protobuf.MethodDescriptorProto.output_type', index=2,
-      number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='options', full_name='google.protobuf.MethodDescriptorProto.options', index=3,
-      number=4, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='client_streaming', full_name='google.protobuf.MethodDescriptorProto.client_streaming', index=4,
-      number=5, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='server_streaming', full_name='google.protobuf.MethodDescriptorProto.server_streaming', index=5,
-      number=6, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2214,
-  serialized_end=2407,
-)
-
-
-_FILEOPTIONS = _descriptor.Descriptor(
-  name='FileOptions',
-  full_name='google.protobuf.FileOptions',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='java_package', full_name='google.protobuf.FileOptions.java_package', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='java_outer_classname', full_name='google.protobuf.FileOptions.java_outer_classname', index=1,
-      number=8, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='java_multiple_files', full_name='google.protobuf.FileOptions.java_multiple_files', index=2,
-      number=10, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='java_generate_equals_and_hash', full_name='google.protobuf.FileOptions.java_generate_equals_and_hash', index=3,
-      number=20, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='java_string_check_utf8', full_name='google.protobuf.FileOptions.java_string_check_utf8', index=4,
-      number=27, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='optimize_for', full_name='google.protobuf.FileOptions.optimize_for', index=5,
-      number=9, type=14, cpp_type=8, label=1,
-      has_default_value=True, default_value=1,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='go_package', full_name='google.protobuf.FileOptions.go_package', index=6,
-      number=11, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='cc_generic_services', full_name='google.protobuf.FileOptions.cc_generic_services', index=7,
-      number=16, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='java_generic_services', full_name='google.protobuf.FileOptions.java_generic_services', index=8,
-      number=17, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='py_generic_services', full_name='google.protobuf.FileOptions.py_generic_services', index=9,
-      number=18, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='deprecated', full_name='google.protobuf.FileOptions.deprecated', index=10,
-      number=23, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='cc_enable_arenas', full_name='google.protobuf.FileOptions.cc_enable_arenas', index=11,
-      number=31, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='uninterpreted_option', full_name='google.protobuf.FileOptions.uninterpreted_option', index=12,
-      number=999, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _FILEOPTIONS_OPTIMIZEMODE,
-  ],
-  options=None,
-  is_extendable=True,
-  syntax='proto2',
-  extension_ranges=[(1000, 536870912), ],
-  oneofs=[
-  ],
-  serialized_start=2410,
-  serialized_end=2998,
-)
-
-
-_MESSAGEOPTIONS = _descriptor.Descriptor(
-  name='MessageOptions',
-  full_name='google.protobuf.MessageOptions',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='message_set_wire_format', full_name='google.protobuf.MessageOptions.message_set_wire_format', index=0,
-      number=1, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='no_standard_descriptor_accessor', full_name='google.protobuf.MessageOptions.no_standard_descriptor_accessor', index=1,
-      number=2, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='deprecated', full_name='google.protobuf.MessageOptions.deprecated', index=2,
-      number=3, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='map_entry', full_name='google.protobuf.MessageOptions.map_entry', index=3,
-      number=7, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='uninterpreted_option', full_name='google.protobuf.MessageOptions.uninterpreted_option', index=4,
-      number=999, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=True,
-  syntax='proto2',
-  extension_ranges=[(1000, 536870912), ],
-  oneofs=[
-  ],
-  serialized_start=3001,
-  serialized_end=3231,
-)
-
-
-_FIELDOPTIONS = _descriptor.Descriptor(
-  name='FieldOptions',
-  full_name='google.protobuf.FieldOptions',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='ctype', full_name='google.protobuf.FieldOptions.ctype', index=0,
-      number=1, type=14, cpp_type=8, label=1,
-      has_default_value=True, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='packed', full_name='google.protobuf.FieldOptions.packed', index=1,
-      number=2, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='lazy', full_name='google.protobuf.FieldOptions.lazy', index=2,
-      number=5, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='deprecated', full_name='google.protobuf.FieldOptions.deprecated', index=3,
-      number=3, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='weak', full_name='google.protobuf.FieldOptions.weak', index=4,
-      number=10, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='uninterpreted_option', full_name='google.protobuf.FieldOptions.uninterpreted_option', index=5,
-      number=999, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _FIELDOPTIONS_CTYPE,
-  ],
-  options=None,
-  is_extendable=True,
-  syntax='proto2',
-  extension_ranges=[(1000, 536870912), ],
-  oneofs=[
-  ],
-  serialized_start=3234,
-  serialized_end=3522,
-)
-
-
-_ENUMOPTIONS = _descriptor.Descriptor(
-  name='EnumOptions',
-  full_name='google.protobuf.EnumOptions',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='allow_alias', full_name='google.protobuf.EnumOptions.allow_alias', index=0,
-      number=2, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='deprecated', full_name='google.protobuf.EnumOptions.deprecated', index=1,
-      number=3, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='uninterpreted_option', full_name='google.protobuf.EnumOptions.uninterpreted_option', index=2,
-      number=999, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=True,
-  syntax='proto2',
-  extension_ranges=[(1000, 536870912), ],
-  oneofs=[
-  ],
-  serialized_start=3525,
-  serialized_end=3666,
-)
-
-
-_ENUMVALUEOPTIONS = _descriptor.Descriptor(
-  name='EnumValueOptions',
-  full_name='google.protobuf.EnumValueOptions',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='deprecated', full_name='google.protobuf.EnumValueOptions.deprecated', index=0,
-      number=1, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='uninterpreted_option', full_name='google.protobuf.EnumValueOptions.uninterpreted_option', index=1,
-      number=999, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=True,
-  syntax='proto2',
-  extension_ranges=[(1000, 536870912), ],
-  oneofs=[
-  ],
-  serialized_start=3668,
-  serialized_end=3793,
-)
-
-
-_SERVICEOPTIONS = _descriptor.Descriptor(
-  name='ServiceOptions',
-  full_name='google.protobuf.ServiceOptions',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='deprecated', full_name='google.protobuf.ServiceOptions.deprecated', index=0,
-      number=33, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='uninterpreted_option', full_name='google.protobuf.ServiceOptions.uninterpreted_option', index=1,
-      number=999, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=True,
-  syntax='proto2',
-  extension_ranges=[(1000, 536870912), ],
-  oneofs=[
-  ],
-  serialized_start=3795,
-  serialized_end=3918,
-)
-
-
-_METHODOPTIONS = _descriptor.Descriptor(
-  name='MethodOptions',
-  full_name='google.protobuf.MethodOptions',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='deprecated', full_name='google.protobuf.MethodOptions.deprecated', index=0,
-      number=33, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='uninterpreted_option', full_name='google.protobuf.MethodOptions.uninterpreted_option', index=1,
-      number=999, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=True,
-  syntax='proto2',
-  extension_ranges=[(1000, 536870912), ],
-  oneofs=[
-  ],
-  serialized_start=3920,
-  serialized_end=4042,
-)
-
-
-_UNINTERPRETEDOPTION_NAMEPART = _descriptor.Descriptor(
-  name='NamePart',
-  full_name='google.protobuf.UninterpretedOption.NamePart',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name_part', full_name='google.protobuf.UninterpretedOption.NamePart.name_part', index=0,
-      number=1, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='is_extension', full_name='google.protobuf.UninterpretedOption.NamePart.is_extension', index=1,
-      number=2, type=8, cpp_type=7, label=2,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=4280,
-  serialized_end=4331,
-)
-
-_UNINTERPRETEDOPTION = _descriptor.Descriptor(
-  name='UninterpretedOption',
-  full_name='google.protobuf.UninterpretedOption',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='google.protobuf.UninterpretedOption.name', index=0,
-      number=2, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='identifier_value', full_name='google.protobuf.UninterpretedOption.identifier_value', index=1,
-      number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='positive_int_value', full_name='google.protobuf.UninterpretedOption.positive_int_value', index=2,
-      number=4, type=4, cpp_type=4, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='negative_int_value', full_name='google.protobuf.UninterpretedOption.negative_int_value', index=3,
-      number=5, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='double_value', full_name='google.protobuf.UninterpretedOption.double_value', index=4,
-      number=6, type=1, cpp_type=5, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='string_value', full_name='google.protobuf.UninterpretedOption.string_value', index=5,
-      number=7, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b(""),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='aggregate_value', full_name='google.protobuf.UninterpretedOption.aggregate_value', index=6,
-      number=8, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[_UNINTERPRETEDOPTION_NAMEPART, ],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=4045,
-  serialized_end=4331,
-)
-
-
-_SOURCECODEINFO_LOCATION = _descriptor.Descriptor(
-  name='Location',
-  full_name='google.protobuf.SourceCodeInfo.Location',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='path', full_name='google.protobuf.SourceCodeInfo.Location.path', index=0,
-      number=1, type=5, cpp_type=1, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='span', full_name='google.protobuf.SourceCodeInfo.Location.span', index=1,
-      number=2, type=5, cpp_type=1, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='leading_comments', full_name='google.protobuf.SourceCodeInfo.Location.leading_comments', index=2,
-      number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='trailing_comments', full_name='google.protobuf.SourceCodeInfo.Location.trailing_comments', index=3,
-      number=4, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=4412,
-  serialized_end=4511,
-)
-
-_SOURCECODEINFO = _descriptor.Descriptor(
-  name='SourceCodeInfo',
-  full_name='google.protobuf.SourceCodeInfo',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='location', full_name='google.protobuf.SourceCodeInfo.location', index=0,
-      number=1, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[_SOURCECODEINFO_LOCATION, ],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=4334,
-  serialized_end=4511,
-)
-
-_FILEDESCRIPTORSET.fields_by_name['file'].message_type = _FILEDESCRIPTORPROTO
-_FILEDESCRIPTORPROTO.fields_by_name['message_type'].message_type = _DESCRIPTORPROTO
-_FILEDESCRIPTORPROTO.fields_by_name['enum_type'].message_type = _ENUMDESCRIPTORPROTO
-_FILEDESCRIPTORPROTO.fields_by_name['service'].message_type = _SERVICEDESCRIPTORPROTO
-_FILEDESCRIPTORPROTO.fields_by_name['extension'].message_type = _FIELDDESCRIPTORPROTO
-_FILEDESCRIPTORPROTO.fields_by_name['options'].message_type = _FILEOPTIONS
-_FILEDESCRIPTORPROTO.fields_by_name['source_code_info'].message_type = _SOURCECODEINFO
-_DESCRIPTORPROTO_EXTENSIONRANGE.containing_type = _DESCRIPTORPROTO
-_DESCRIPTORPROTO.fields_by_name['field'].message_type = _FIELDDESCRIPTORPROTO
-_DESCRIPTORPROTO.fields_by_name['extension'].message_type = _FIELDDESCRIPTORPROTO
-_DESCRIPTORPROTO.fields_by_name['nested_type'].message_type = _DESCRIPTORPROTO
-_DESCRIPTORPROTO.fields_by_name['enum_type'].message_type = _ENUMDESCRIPTORPROTO
-_DESCRIPTORPROTO.fields_by_name['extension_range'].message_type = _DESCRIPTORPROTO_EXTENSIONRANGE
-_DESCRIPTORPROTO.fields_by_name['oneof_decl'].message_type = _ONEOFDESCRIPTORPROTO
-_DESCRIPTORPROTO.fields_by_name['options'].message_type = _MESSAGEOPTIONS
-_FIELDDESCRIPTORPROTO.fields_by_name['label'].enum_type = _FIELDDESCRIPTORPROTO_LABEL
-_FIELDDESCRIPTORPROTO.fields_by_name['type'].enum_type = _FIELDDESCRIPTORPROTO_TYPE
-_FIELDDESCRIPTORPROTO.fields_by_name['options'].message_type = _FIELDOPTIONS
-_FIELDDESCRIPTORPROTO_TYPE.containing_type = _FIELDDESCRIPTORPROTO
-_FIELDDESCRIPTORPROTO_LABEL.containing_type = _FIELDDESCRIPTORPROTO
-_ENUMDESCRIPTORPROTO.fields_by_name['value'].message_type = _ENUMVALUEDESCRIPTORPROTO
-_ENUMDESCRIPTORPROTO.fields_by_name['options'].message_type = _ENUMOPTIONS
-_ENUMVALUEDESCRIPTORPROTO.fields_by_name['options'].message_type = _ENUMVALUEOPTIONS
-_SERVICEDESCRIPTORPROTO.fields_by_name['method'].message_type = _METHODDESCRIPTORPROTO
-_SERVICEDESCRIPTORPROTO.fields_by_name['options'].message_type = _SERVICEOPTIONS
-_METHODDESCRIPTORPROTO.fields_by_name['options'].message_type = _METHODOPTIONS
-_FILEOPTIONS.fields_by_name['optimize_for'].enum_type = _FILEOPTIONS_OPTIMIZEMODE
-_FILEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
-_FILEOPTIONS_OPTIMIZEMODE.containing_type = _FILEOPTIONS
-_MESSAGEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
-_FIELDOPTIONS.fields_by_name['ctype'].enum_type = _FIELDOPTIONS_CTYPE
-_FIELDOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
-_FIELDOPTIONS_CTYPE.containing_type = _FIELDOPTIONS
-_ENUMOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
-_ENUMVALUEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
-_SERVICEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
-_METHODOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
-_UNINTERPRETEDOPTION_NAMEPART.containing_type = _UNINTERPRETEDOPTION
-_UNINTERPRETEDOPTION.fields_by_name['name'].message_type = _UNINTERPRETEDOPTION_NAMEPART
-_SOURCECODEINFO_LOCATION.containing_type = _SOURCECODEINFO
-_SOURCECODEINFO.fields_by_name['location'].message_type = _SOURCECODEINFO_LOCATION
-DESCRIPTOR.message_types_by_name['FileDescriptorSet'] = _FILEDESCRIPTORSET
-DESCRIPTOR.message_types_by_name['FileDescriptorProto'] = _FILEDESCRIPTORPROTO
-DESCRIPTOR.message_types_by_name['DescriptorProto'] = _DESCRIPTORPROTO
-DESCRIPTOR.message_types_by_name['FieldDescriptorProto'] = _FIELDDESCRIPTORPROTO
-DESCRIPTOR.message_types_by_name['OneofDescriptorProto'] = _ONEOFDESCRIPTORPROTO
-DESCRIPTOR.message_types_by_name['EnumDescriptorProto'] = _ENUMDESCRIPTORPROTO
-DESCRIPTOR.message_types_by_name['EnumValueDescriptorProto'] = _ENUMVALUEDESCRIPTORPROTO
-DESCRIPTOR.message_types_by_name['ServiceDescriptorProto'] = _SERVICEDESCRIPTORPROTO
-DESCRIPTOR.message_types_by_name['MethodDescriptorProto'] = _METHODDESCRIPTORPROTO
-DESCRIPTOR.message_types_by_name['FileOptions'] = _FILEOPTIONS
-DESCRIPTOR.message_types_by_name['MessageOptions'] = _MESSAGEOPTIONS
-DESCRIPTOR.message_types_by_name['FieldOptions'] = _FIELDOPTIONS
-DESCRIPTOR.message_types_by_name['EnumOptions'] = _ENUMOPTIONS
-DESCRIPTOR.message_types_by_name['EnumValueOptions'] = _ENUMVALUEOPTIONS
-DESCRIPTOR.message_types_by_name['ServiceOptions'] = _SERVICEOPTIONS
-DESCRIPTOR.message_types_by_name['MethodOptions'] = _METHODOPTIONS
-DESCRIPTOR.message_types_by_name['UninterpretedOption'] = _UNINTERPRETEDOPTION
-DESCRIPTOR.message_types_by_name['SourceCodeInfo'] = _SOURCECODEINFO
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-FileDescriptorSet = _reflection.GeneratedProtocolMessageType('FileDescriptorSet', (_message.Message,), dict(
-  DESCRIPTOR = _FILEDESCRIPTORSET,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.FileDescriptorSet)
-  ))
-_sym_db.RegisterMessage(FileDescriptorSet)
-
-FileDescriptorProto = _reflection.GeneratedProtocolMessageType('FileDescriptorProto', (_message.Message,), dict(
-  DESCRIPTOR = _FILEDESCRIPTORPROTO,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.FileDescriptorProto)
-  ))
-_sym_db.RegisterMessage(FileDescriptorProto)
+  _FILEOPTIONS = _descriptor.Descriptor(
+    name='FileOptions',
+    full_name='google.protobuf.FileOptions',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='java_package', full_name='google.protobuf.FileOptions.java_package', index=0,
+        number=1, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='java_outer_classname', full_name='google.protobuf.FileOptions.java_outer_classname', index=1,
+        number=8, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='java_multiple_files', full_name='google.protobuf.FileOptions.java_multiple_files', index=2,
+        number=10, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='java_generate_equals_and_hash', full_name='google.protobuf.FileOptions.java_generate_equals_and_hash', index=3,
+        number=20, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='java_string_check_utf8', full_name='google.protobuf.FileOptions.java_string_check_utf8', index=4,
+        number=27, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='optimize_for', full_name='google.protobuf.FileOptions.optimize_for', index=5,
+        number=9, type=14, cpp_type=8, label=1,
+        has_default_value=True, default_value=1,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='go_package', full_name='google.protobuf.FileOptions.go_package', index=6,
+        number=11, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='cc_generic_services', full_name='google.protobuf.FileOptions.cc_generic_services', index=7,
+        number=16, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='java_generic_services', full_name='google.protobuf.FileOptions.java_generic_services', index=8,
+        number=17, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='py_generic_services', full_name='google.protobuf.FileOptions.py_generic_services', index=9,
+        number=18, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='deprecated', full_name='google.protobuf.FileOptions.deprecated', index=10,
+        number=23, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='cc_enable_arenas', full_name='google.protobuf.FileOptions.cc_enable_arenas', index=11,
+        number=31, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='uninterpreted_option', full_name='google.protobuf.FileOptions.uninterpreted_option', index=12,
+        number=999, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+      _FILEOPTIONS_OPTIMIZEMODE,
+    ],
+    serialized_options=None,
+    is_extendable=True,
+    syntax='proto2',
+    extension_ranges=[(1000, 536870912), ],
+    oneofs=[
+    ],
+  )
 
-DescriptorProto = _reflection.GeneratedProtocolMessageType('DescriptorProto', (_message.Message,), dict(
 
-  ExtensionRange = _reflection.GeneratedProtocolMessageType('ExtensionRange', (_message.Message,), dict(
-    DESCRIPTOR = _DESCRIPTORPROTO_EXTENSIONRANGE,
-    __module__ = 'google.protobuf.descriptor_pb2'
-    # @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto.ExtensionRange)
-    ))
-  ,
-  DESCRIPTOR = _DESCRIPTORPROTO,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.DescriptorProto)
-  ))
-_sym_db.RegisterMessage(DescriptorProto)
-_sym_db.RegisterMessage(DescriptorProto.ExtensionRange)
+  _MESSAGEOPTIONS = _descriptor.Descriptor(
+    name='MessageOptions',
+    full_name='google.protobuf.MessageOptions',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='message_set_wire_format', full_name='google.protobuf.MessageOptions.message_set_wire_format', index=0,
+        number=1, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='no_standard_descriptor_accessor', full_name='google.protobuf.MessageOptions.no_standard_descriptor_accessor', index=1,
+        number=2, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='deprecated', full_name='google.protobuf.MessageOptions.deprecated', index=2,
+        number=3, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='map_entry', full_name='google.protobuf.MessageOptions.map_entry', index=3,
+        number=7, type=8, cpp_type=7, label=1,
+        has_default_value=False, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='uninterpreted_option', full_name='google.protobuf.MessageOptions.uninterpreted_option', index=4,
+        number=999, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=True,
+    syntax='proto2',
+    extension_ranges=[(1000, 536870912), ],
+    oneofs=[
+    ],
+  )
 
-FieldDescriptorProto = _reflection.GeneratedProtocolMessageType('FieldDescriptorProto', (_message.Message,), dict(
-  DESCRIPTOR = _FIELDDESCRIPTORPROTO,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.FieldDescriptorProto)
-  ))
-_sym_db.RegisterMessage(FieldDescriptorProto)
 
-OneofDescriptorProto = _reflection.GeneratedProtocolMessageType('OneofDescriptorProto', (_message.Message,), dict(
-  DESCRIPTOR = _ONEOFDESCRIPTORPROTO,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.OneofDescriptorProto)
-  ))
-_sym_db.RegisterMessage(OneofDescriptorProto)
+  _FIELDOPTIONS = _descriptor.Descriptor(
+    name='FieldOptions',
+    full_name='google.protobuf.FieldOptions',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='ctype', full_name='google.protobuf.FieldOptions.ctype', index=0,
+        number=1, type=14, cpp_type=8, label=1,
+        has_default_value=True, default_value=0,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='packed', full_name='google.protobuf.FieldOptions.packed', index=1,
+        number=2, type=8, cpp_type=7, label=1,
+        has_default_value=False, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='lazy', full_name='google.protobuf.FieldOptions.lazy', index=2,
+        number=5, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='deprecated', full_name='google.protobuf.FieldOptions.deprecated', index=3,
+        number=3, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='weak', full_name='google.protobuf.FieldOptions.weak', index=4,
+        number=10, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='uninterpreted_option', full_name='google.protobuf.FieldOptions.uninterpreted_option', index=5,
+        number=999, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+      _FIELDOPTIONS_CTYPE,
+    ],
+    serialized_options=None,
+    is_extendable=True,
+    syntax='proto2',
+    extension_ranges=[(1000, 536870912), ],
+    oneofs=[
+    ],
+  )
 
-EnumDescriptorProto = _reflection.GeneratedProtocolMessageType('EnumDescriptorProto', (_message.Message,), dict(
-  DESCRIPTOR = _ENUMDESCRIPTORPROTO,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.EnumDescriptorProto)
-  ))
-_sym_db.RegisterMessage(EnumDescriptorProto)
 
-EnumValueDescriptorProto = _reflection.GeneratedProtocolMessageType('EnumValueDescriptorProto', (_message.Message,), dict(
-  DESCRIPTOR = _ENUMVALUEDESCRIPTORPROTO,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.EnumValueDescriptorProto)
-  ))
-_sym_db.RegisterMessage(EnumValueDescriptorProto)
+  _ENUMOPTIONS = _descriptor.Descriptor(
+    name='EnumOptions',
+    full_name='google.protobuf.EnumOptions',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='allow_alias', full_name='google.protobuf.EnumOptions.allow_alias', index=0,
+        number=2, type=8, cpp_type=7, label=1,
+        has_default_value=False, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='deprecated', full_name='google.protobuf.EnumOptions.deprecated', index=1,
+        number=3, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='uninterpreted_option', full_name='google.protobuf.EnumOptions.uninterpreted_option', index=2,
+        number=999, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=True,
+    syntax='proto2',
+    extension_ranges=[(1000, 536870912), ],
+    oneofs=[
+    ],
+  )
 
-ServiceDescriptorProto = _reflection.GeneratedProtocolMessageType('ServiceDescriptorProto', (_message.Message,), dict(
-  DESCRIPTOR = _SERVICEDESCRIPTORPROTO,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.ServiceDescriptorProto)
-  ))
-_sym_db.RegisterMessage(ServiceDescriptorProto)
 
-MethodDescriptorProto = _reflection.GeneratedProtocolMessageType('MethodDescriptorProto', (_message.Message,), dict(
-  DESCRIPTOR = _METHODDESCRIPTORPROTO,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.MethodDescriptorProto)
-  ))
-_sym_db.RegisterMessage(MethodDescriptorProto)
+  _ENUMVALUEOPTIONS = _descriptor.Descriptor(
+    name='EnumValueOptions',
+    full_name='google.protobuf.EnumValueOptions',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='deprecated', full_name='google.protobuf.EnumValueOptions.deprecated', index=0,
+        number=1, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='uninterpreted_option', full_name='google.protobuf.EnumValueOptions.uninterpreted_option', index=1,
+        number=999, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=True,
+    syntax='proto2',
+    extension_ranges=[(1000, 536870912), ],
+    oneofs=[
+    ],
+  )
 
-FileOptions = _reflection.GeneratedProtocolMessageType('FileOptions', (_message.Message,), dict(
-  DESCRIPTOR = _FILEOPTIONS,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.FileOptions)
-  ))
-_sym_db.RegisterMessage(FileOptions)
 
-MessageOptions = _reflection.GeneratedProtocolMessageType('MessageOptions', (_message.Message,), dict(
-  DESCRIPTOR = _MESSAGEOPTIONS,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.MessageOptions)
-  ))
-_sym_db.RegisterMessage(MessageOptions)
+  _SERVICEOPTIONS = _descriptor.Descriptor(
+    name='ServiceOptions',
+    full_name='google.protobuf.ServiceOptions',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='deprecated', full_name='google.protobuf.ServiceOptions.deprecated', index=0,
+        number=33, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='uninterpreted_option', full_name='google.protobuf.ServiceOptions.uninterpreted_option', index=1,
+        number=999, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=True,
+    syntax='proto2',
+    extension_ranges=[(1000, 536870912), ],
+    oneofs=[
+    ],
+  )
 
-FieldOptions = _reflection.GeneratedProtocolMessageType('FieldOptions', (_message.Message,), dict(
-  DESCRIPTOR = _FIELDOPTIONS,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.FieldOptions)
-  ))
-_sym_db.RegisterMessage(FieldOptions)
 
-EnumOptions = _reflection.GeneratedProtocolMessageType('EnumOptions', (_message.Message,), dict(
-  DESCRIPTOR = _ENUMOPTIONS,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.EnumOptions)
-  ))
-_sym_db.RegisterMessage(EnumOptions)
+  _METHODOPTIONS = _descriptor.Descriptor(
+    name='MethodOptions',
+    full_name='google.protobuf.MethodOptions',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='deprecated', full_name='google.protobuf.MethodOptions.deprecated', index=0,
+        number=33, type=8, cpp_type=7, label=1,
+        has_default_value=True, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='uninterpreted_option', full_name='google.protobuf.MethodOptions.uninterpreted_option', index=1,
+        number=999, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=True,
+    syntax='proto2',
+    extension_ranges=[(1000, 536870912), ],
+    oneofs=[
+    ],
+  )
 
-EnumValueOptions = _reflection.GeneratedProtocolMessageType('EnumValueOptions', (_message.Message,), dict(
-  DESCRIPTOR = _ENUMVALUEOPTIONS,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.EnumValueOptions)
-  ))
-_sym_db.RegisterMessage(EnumValueOptions)
 
-ServiceOptions = _reflection.GeneratedProtocolMessageType('ServiceOptions', (_message.Message,), dict(
-  DESCRIPTOR = _SERVICEOPTIONS,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.ServiceOptions)
-  ))
-_sym_db.RegisterMessage(ServiceOptions)
+  _UNINTERPRETEDOPTION_NAMEPART = _descriptor.Descriptor(
+    name='NamePart',
+    full_name='google.protobuf.UninterpretedOption.NamePart',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='name_part', full_name='google.protobuf.UninterpretedOption.NamePart.name_part', index=0,
+        number=1, type=9, cpp_type=9, label=2,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='is_extension', full_name='google.protobuf.UninterpretedOption.NamePart.is_extension', index=1,
+        number=2, type=8, cpp_type=7, label=2,
+        has_default_value=False, default_value=False,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
-MethodOptions = _reflection.GeneratedProtocolMessageType('MethodOptions', (_message.Message,), dict(
-  DESCRIPTOR = _METHODOPTIONS,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.MethodOptions)
-  ))
-_sym_db.RegisterMessage(MethodOptions)
+  _UNINTERPRETEDOPTION = _descriptor.Descriptor(
+    name='UninterpretedOption',
+    full_name='google.protobuf.UninterpretedOption',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='name', full_name='google.protobuf.UninterpretedOption.name', index=0,
+        number=2, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='identifier_value', full_name='google.protobuf.UninterpretedOption.identifier_value', index=1,
+        number=3, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='positive_int_value', full_name='google.protobuf.UninterpretedOption.positive_int_value', index=2,
+        number=4, type=4, cpp_type=4, label=1,
+        has_default_value=False, default_value=0,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='negative_int_value', full_name='google.protobuf.UninterpretedOption.negative_int_value', index=3,
+        number=5, type=3, cpp_type=2, label=1,
+        has_default_value=False, default_value=0,
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='double_value', full_name='google.protobuf.UninterpretedOption.double_value', index=4,
+        number=6, type=1, cpp_type=5, label=1,
+        has_default_value=False, default_value=float(0),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='string_value', full_name='google.protobuf.UninterpretedOption.string_value', index=5,
+        number=7, type=12, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"",
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='aggregate_value', full_name='google.protobuf.UninterpretedOption.aggregate_value', index=6,
+        number=8, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[_UNINTERPRETEDOPTION_NAMEPART, ],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
-UninterpretedOption = _reflection.GeneratedProtocolMessageType('UninterpretedOption', (_message.Message,), dict(
 
-  NamePart = _reflection.GeneratedProtocolMessageType('NamePart', (_message.Message,), dict(
-    DESCRIPTOR = _UNINTERPRETEDOPTION_NAMEPART,
-    __module__ = 'google.protobuf.descriptor_pb2'
-    # @@protoc_insertion_point(class_scope:google.protobuf.UninterpretedOption.NamePart)
-    ))
-  ,
-  DESCRIPTOR = _UNINTERPRETEDOPTION,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.UninterpretedOption)
-  ))
-_sym_db.RegisterMessage(UninterpretedOption)
-_sym_db.RegisterMessage(UninterpretedOption.NamePart)
+  _SOURCECODEINFO_LOCATION = _descriptor.Descriptor(
+    name='Location',
+    full_name='google.protobuf.SourceCodeInfo.Location',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='path', full_name='google.protobuf.SourceCodeInfo.Location.path', index=0,
+        number=1, type=5, cpp_type=1, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='span', full_name='google.protobuf.SourceCodeInfo.Location.span', index=1,
+        number=2, type=5, cpp_type=1, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='leading_comments', full_name='google.protobuf.SourceCodeInfo.Location.leading_comments', index=2,
+        number=3, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+      _descriptor.FieldDescriptor(
+        name='trailing_comments', full_name='google.protobuf.SourceCodeInfo.Location.trailing_comments', index=3,
+        number=4, type=9, cpp_type=9, label=1,
+        has_default_value=False, default_value=b"".decode('utf-8'),
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
-SourceCodeInfo = _reflection.GeneratedProtocolMessageType('SourceCodeInfo', (_message.Message,), dict(
+  _SOURCECODEINFO = _descriptor.Descriptor(
+    name='SourceCodeInfo',
+    full_name='google.protobuf.SourceCodeInfo',
+    filename=None,
+    file=DESCRIPTOR,
+    containing_type=None,
+    create_key=_descriptor._internal_create_key,
+    fields=[
+      _descriptor.FieldDescriptor(
+        name='location', full_name='google.protobuf.SourceCodeInfo.location', index=0,
+        number=1, type=11, cpp_type=10, label=3,
+        has_default_value=False, default_value=[],
+        message_type=None, enum_type=None, containing_type=None,
+        is_extension=False, extension_scope=None,
+        serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+    ],
+    extensions=[
+    ],
+    nested_types=[_SOURCECODEINFO_LOCATION, ],
+    enum_types=[
+    ],
+    serialized_options=None,
+    is_extendable=False,
+    syntax='proto2',
+    extension_ranges=[],
+    oneofs=[
+    ],
+  )
 
-  Location = _reflection.GeneratedProtocolMessageType('Location', (_message.Message,), dict(
-    DESCRIPTOR = _SOURCECODEINFO_LOCATION,
-    __module__ = 'google.protobuf.descriptor_pb2'
-    # @@protoc_insertion_point(class_scope:google.protobuf.SourceCodeInfo.Location)
-    ))
-  ,
-  DESCRIPTOR = _SOURCECODEINFO,
-  __module__ = 'google.protobuf.descriptor_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.SourceCodeInfo)
-  ))
-_sym_db.RegisterMessage(SourceCodeInfo)
-_sym_db.RegisterMessage(SourceCodeInfo.Location)
+  _FILEDESCRIPTORSET.fields_by_name['file'].message_type = _FILEDESCRIPTORPROTO
+  _FILEDESCRIPTORPROTO.fields_by_name['message_type'].message_type = _DESCRIPTORPROTO
+  _FILEDESCRIPTORPROTO.fields_by_name['enum_type'].message_type = _ENUMDESCRIPTORPROTO
+  _FILEDESCRIPTORPROTO.fields_by_name['service'].message_type = _SERVICEDESCRIPTORPROTO
+  _FILEDESCRIPTORPROTO.fields_by_name['extension'].message_type = _FIELDDESCRIPTORPROTO
+  _FILEDESCRIPTORPROTO.fields_by_name['options'].message_type = _FILEOPTIONS
+  _FILEDESCRIPTORPROTO.fields_by_name['source_code_info'].message_type = _SOURCECODEINFO
+  _DESCRIPTORPROTO_EXTENSIONRANGE.containing_type = _DESCRIPTORPROTO
+  _DESCRIPTORPROTO.fields_by_name['field'].message_type = _FIELDDESCRIPTORPROTO
+  _DESCRIPTORPROTO.fields_by_name['extension'].message_type = _FIELDDESCRIPTORPROTO
+  _DESCRIPTORPROTO.fields_by_name['nested_type'].message_type = _DESCRIPTORPROTO
+  _DESCRIPTORPROTO.fields_by_name['enum_type'].message_type = _ENUMDESCRIPTORPROTO
+  _DESCRIPTORPROTO.fields_by_name['extension_range'].message_type = _DESCRIPTORPROTO_EXTENSIONRANGE
+  _DESCRIPTORPROTO.fields_by_name['oneof_decl'].message_type = _ONEOFDESCRIPTORPROTO
+  _DESCRIPTORPROTO.fields_by_name['options'].message_type = _MESSAGEOPTIONS
+  _FIELDDESCRIPTORPROTO.fields_by_name['label'].enum_type = _FIELDDESCRIPTORPROTO_LABEL
+  _FIELDDESCRIPTORPROTO.fields_by_name['type'].enum_type = _FIELDDESCRIPTORPROTO_TYPE
+  _FIELDDESCRIPTORPROTO.fields_by_name['options'].message_type = _FIELDOPTIONS
+  _FIELDDESCRIPTORPROTO_TYPE.containing_type = _FIELDDESCRIPTORPROTO
+  _FIELDDESCRIPTORPROTO_LABEL.containing_type = _FIELDDESCRIPTORPROTO
+  _ENUMDESCRIPTORPROTO.fields_by_name['value'].message_type = _ENUMVALUEDESCRIPTORPROTO
+  _ENUMDESCRIPTORPROTO.fields_by_name['options'].message_type = _ENUMOPTIONS
+  _ENUMVALUEDESCRIPTORPROTO.fields_by_name['options'].message_type = _ENUMVALUEOPTIONS
+  _SERVICEDESCRIPTORPROTO.fields_by_name['method'].message_type = _METHODDESCRIPTORPROTO
+  _SERVICEDESCRIPTORPROTO.fields_by_name['options'].message_type = _SERVICEOPTIONS
+  _METHODDESCRIPTORPROTO.fields_by_name['options'].message_type = _METHODOPTIONS
+  _FILEOPTIONS.fields_by_name['optimize_for'].enum_type = _FILEOPTIONS_OPTIMIZEMODE
+  _FILEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
+  _FILEOPTIONS_OPTIMIZEMODE.containing_type = _FILEOPTIONS
+  _MESSAGEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
+  _FIELDOPTIONS.fields_by_name['ctype'].enum_type = _FIELDOPTIONS_CTYPE
+  _FIELDOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
+  _FIELDOPTIONS_CTYPE.containing_type = _FIELDOPTIONS
+  _ENUMOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
+  _ENUMVALUEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
+  _SERVICEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
+  _METHODOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION
+  _UNINTERPRETEDOPTION_NAMEPART.containing_type = _UNINTERPRETEDOPTION
+  _UNINTERPRETEDOPTION.fields_by_name['name'].message_type = _UNINTERPRETEDOPTION_NAMEPART
+  _SOURCECODEINFO_LOCATION.containing_type = _SOURCECODEINFO
+  _SOURCECODEINFO.fields_by_name['location'].message_type = _SOURCECODEINFO_LOCATION
+  DESCRIPTOR.message_types_by_name['FileDescriptorSet'] = _FILEDESCRIPTORSET
+  DESCRIPTOR.message_types_by_name['FileDescriptorProto'] = _FILEDESCRIPTORPROTO
+  DESCRIPTOR.message_types_by_name['DescriptorProto'] = _DESCRIPTORPROTO
+  DESCRIPTOR.message_types_by_name['FieldDescriptorProto'] = _FIELDDESCRIPTORPROTO
+  DESCRIPTOR.message_types_by_name['OneofDescriptorProto'] = _ONEOFDESCRIPTORPROTO
+  DESCRIPTOR.message_types_by_name['EnumDescriptorProto'] = _ENUMDESCRIPTORPROTO
+  DESCRIPTOR.message_types_by_name['EnumValueDescriptorProto'] = _ENUMVALUEDESCRIPTORPROTO
+  DESCRIPTOR.message_types_by_name['ServiceDescriptorProto'] = _SERVICEDESCRIPTORPROTO
+  DESCRIPTOR.message_types_by_name['MethodDescriptorProto'] = _METHODDESCRIPTORPROTO
+  DESCRIPTOR.message_types_by_name['FileOptions'] = _FILEOPTIONS
+  DESCRIPTOR.message_types_by_name['MessageOptions'] = _MESSAGEOPTIONS
+  DESCRIPTOR.message_types_by_name['FieldOptions'] = _FIELDOPTIONS
+  DESCRIPTOR.message_types_by_name['EnumOptions'] = _ENUMOPTIONS
+  DESCRIPTOR.message_types_by_name['EnumValueOptions'] = _ENUMVALUEOPTIONS
+  DESCRIPTOR.message_types_by_name['ServiceOptions'] = _SERVICEOPTIONS
+  DESCRIPTOR.message_types_by_name['MethodOptions'] = _METHODOPTIONS
+  DESCRIPTOR.message_types_by_name['UninterpretedOption'] = _UNINTERPRETEDOPTION
+  DESCRIPTOR.message_types_by_name['SourceCodeInfo'] = _SOURCECODEINFO
+  _sym_db.RegisterFileDescriptor(DESCRIPTOR)
 
+else:
+  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.descriptor_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
+  DESCRIPTOR._options = None
+  _FILEDESCRIPTORSET._serialized_start=53
+  _FILEDESCRIPTORSET._serialized_end=124
+  _FILEDESCRIPTORPROTO._serialized_start=127
+  _FILEDESCRIPTORPROTO._serialized_end=602
+  _DESCRIPTORPROTO._serialized_start=605
+  _DESCRIPTORPROTO._serialized_end=1089
+  _DESCRIPTORPROTO_EXTENSIONRANGE._serialized_start=1045
+  _DESCRIPTORPROTO_EXTENSIONRANGE._serialized_end=1089
+  _FIELDDESCRIPTORPROTO._serialized_start=1092
+  _FIELDDESCRIPTORPROTO._serialized_end=1773
+  _FIELDDESCRIPTORPROTO_TYPE._serialized_start=1394
+  _FIELDDESCRIPTORPROTO_TYPE._serialized_end=1704
+  _FIELDDESCRIPTORPROTO_LABEL._serialized_start=1706
+  _FIELDDESCRIPTORPROTO_LABEL._serialized_end=1773
+  _ONEOFDESCRIPTORPROTO._serialized_start=1775
+  _ONEOFDESCRIPTORPROTO._serialized_end=1811
+  _ENUMDESCRIPTORPROTO._serialized_start=1814
+  _ENUMDESCRIPTORPROTO._serialized_end=1954
+  _ENUMVALUEDESCRIPTORPROTO._serialized_start=1956
+  _ENUMVALUEDESCRIPTORPROTO._serialized_end=2064
+  _SERVICEDESCRIPTORPROTO._serialized_start=2067
+  _SERVICEDESCRIPTORPROTO._serialized_end=2211
+  _METHODDESCRIPTORPROTO._serialized_start=2214
+  _METHODDESCRIPTORPROTO._serialized_end=2407
+  _FILEOPTIONS._serialized_start=2410
+  _FILEOPTIONS._serialized_end=2998
+  _FILEOPTIONS_OPTIMIZEMODE._serialized_start=2929
+  _FILEOPTIONS_OPTIMIZEMODE._serialized_end=2987
+  _MESSAGEOPTIONS._serialized_start=3001
+  _MESSAGEOPTIONS._serialized_end=3231
+  _FIELDOPTIONS._serialized_start=3234
+  _FIELDOPTIONS._serialized_end=3522
+  _FIELDOPTIONS_CTYPE._serialized_start=3464
+  _FIELDOPTIONS_CTYPE._serialized_end=3511
+  _ENUMOPTIONS._serialized_start=3525
+  _ENUMOPTIONS._serialized_end=3666
+  _ENUMVALUEOPTIONS._serialized_start=3668
+  _ENUMVALUEOPTIONS._serialized_end=3793
+  _SERVICEOPTIONS._serialized_start=3795
+  _SERVICEOPTIONS._serialized_end=3918
+  _METHODOPTIONS._serialized_start=3920
+  _METHODOPTIONS._serialized_end=4042
+  _UNINTERPRETEDOPTION._serialized_start=4045
+  _UNINTERPRETEDOPTION._serialized_end=4331
+  _UNINTERPRETEDOPTION_NAMEPART._serialized_start=4280
+  _UNINTERPRETEDOPTION_NAMEPART._serialized_end=4331
+  _SOURCECODEINFO._serialized_start=4334
+  _SOURCECODEINFO._serialized_end=4511
+  _SOURCECODEINFO_LOCATION._serialized_start=4412
+  _SOURCECODEINFO_LOCATION._serialized_end=4511
 # @@protoc_insertion_point(module_scope)
diff --git a/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/nanopb_pb2.py b/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/nanopb_pb2.py
index ddf3569..c23077a 100644
--- a/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/nanopb_pb2.py
+++ b/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/nanopb_pb2.py
@@ -1,14 +1,11 @@
+# -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: nanopb.proto
-
-import sys
-_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
-from google.protobuf.internal import enum_type_wrapper
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
-from google.protobuf import descriptor_pb2
 # @@protoc_insertion_point(imports)
 
 _sym_db = _symbol_database.Default()
@@ -17,242 +14,22 @@
 from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='nanopb.proto',
-  package='',
-  syntax='proto2',
-  serialized_pb=_b('\n\x0cnanopb.proto\x1a google/protobuf/descriptor.proto\"\x80\x02\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12&\n\x08int_size\x18\x07 \x01(\x0e\x32\x08.IntSize:\nIS_DEFAULT\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cskip_message\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x18\n\tno_unions\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05msgid\x18\t \x01(\r*Z\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\x0e\n\nFT_POINTER\x10\x04\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03*D\n\x07IntSize\x12\x0e\n\nIS_DEFAULT\x10\x00\x12\x08\n\x04IS_8\x10\x08\x12\t\n\x05IS_16\x10\x10\x12\t\n\x05IS_32\x10 \x12\t\n\x05IS_64\x10@:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptionsB\x1a\n\x18\x66i.kapsi.koti.jpa.nanopb')
-  ,
-  dependencies=[google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,])
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cnanopb.proto\x1a google/protobuf/descriptor.proto\"\x80\x02\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12&\n\x08int_size\x18\x07 \x01(\x0e\x32\x08.IntSize:\nIS_DEFAULT\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cskip_message\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x18\n\tno_unions\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05msgid\x18\t \x01(\r*Z\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\x0e\n\nFT_POINTER\x10\x04\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03*D\n\x07IntSize\x12\x0e\n\nIS_DEFAULT\x10\x00\x12\x08\n\x04IS_8\x10\x08\x12\t\n\x05IS_16\x10\x10\x12\t\n\x05IS_32\x10 \x12\t\n\x05IS_64\x10@:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptionsB\x1a\n\x18\x66i.kapsi.koti.jpa.nanopb')
 
-_FIELDTYPE = _descriptor.EnumDescriptor(
-  name='FieldType',
-  full_name='FieldType',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='FT_DEFAULT', index=0, number=0,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='FT_CALLBACK', index=1, number=1,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='FT_POINTER', index=2, number=4,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='FT_STATIC', index=3, number=2,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='FT_IGNORE', index=4, number=3,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=309,
-  serialized_end=399,
-)
-_sym_db.RegisterEnumDescriptor(_FIELDTYPE)
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'nanopb_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
+  google_dot_protobuf_dot_descriptor__pb2.FileOptions.RegisterExtension(nanopb_fileopt)
+  google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(nanopb_msgopt)
+  google_dot_protobuf_dot_descriptor__pb2.EnumOptions.RegisterExtension(nanopb_enumopt)
+  google_dot_protobuf_dot_descriptor__pb2.FieldOptions.RegisterExtension(nanopb)
 
-FieldType = enum_type_wrapper.EnumTypeWrapper(_FIELDTYPE)
-_INTSIZE = _descriptor.EnumDescriptor(
-  name='IntSize',
-  full_name='IntSize',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='IS_DEFAULT', index=0, number=0,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='IS_8', index=1, number=8,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='IS_16', index=2, number=16,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='IS_32', index=3, number=32,
-      options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='IS_64', index=4, number=64,
-      options=None,
-      type=None),
-  ],
-  containing_type=None,
-  options=None,
-  serialized_start=401,
-  serialized_end=469,
-)
-_sym_db.RegisterEnumDescriptor(_INTSIZE)
-
-IntSize = enum_type_wrapper.EnumTypeWrapper(_INTSIZE)
-FT_DEFAULT = 0
-FT_CALLBACK = 1
-FT_POINTER = 4
-FT_STATIC = 2
-FT_IGNORE = 3
-IS_DEFAULT = 0
-IS_8 = 8
-IS_16 = 16
-IS_32 = 32
-IS_64 = 64
-
-NANOPB_FILEOPT_FIELD_NUMBER = 1010
-nanopb_fileopt = _descriptor.FieldDescriptor(
-  name='nanopb_fileopt', full_name='nanopb_fileopt', index=0,
-  number=1010, type=11, cpp_type=10, label=1,
-  has_default_value=False, default_value=None,
-  message_type=None, enum_type=None, containing_type=None,
-  is_extension=True, extension_scope=None,
-  options=None)
-NANOPB_MSGOPT_FIELD_NUMBER = 1010
-nanopb_msgopt = _descriptor.FieldDescriptor(
-  name='nanopb_msgopt', full_name='nanopb_msgopt', index=1,
-  number=1010, type=11, cpp_type=10, label=1,
-  has_default_value=False, default_value=None,
-  message_type=None, enum_type=None, containing_type=None,
-  is_extension=True, extension_scope=None,
-  options=None)
-NANOPB_ENUMOPT_FIELD_NUMBER = 1010
-nanopb_enumopt = _descriptor.FieldDescriptor(
-  name='nanopb_enumopt', full_name='nanopb_enumopt', index=2,
-  number=1010, type=11, cpp_type=10, label=1,
-  has_default_value=False, default_value=None,
-  message_type=None, enum_type=None, containing_type=None,
-  is_extension=True, extension_scope=None,
-  options=None)
-NANOPB_FIELD_NUMBER = 1010
-nanopb = _descriptor.FieldDescriptor(
-  name='nanopb', full_name='nanopb', index=3,
-  number=1010, type=11, cpp_type=10, label=1,
-  has_default_value=False, default_value=None,
-  message_type=None, enum_type=None, containing_type=None,
-  is_extension=True, extension_scope=None,
-  options=None)
-
-
-_NANOPBOPTIONS = _descriptor.Descriptor(
-  name='NanoPBOptions',
-  full_name='NanoPBOptions',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='max_size', full_name='NanoPBOptions.max_size', index=0,
-      number=1, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='max_count', full_name='NanoPBOptions.max_count', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='int_size', full_name='NanoPBOptions.int_size', index=2,
-      number=7, type=14, cpp_type=8, label=1,
-      has_default_value=True, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='type', full_name='NanoPBOptions.type', index=3,
-      number=3, type=14, cpp_type=8, label=1,
-      has_default_value=True, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='long_names', full_name='NanoPBOptions.long_names', index=4,
-      number=4, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=True,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='packed_struct', full_name='NanoPBOptions.packed_struct', index=5,
-      number=5, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='skip_message', full_name='NanoPBOptions.skip_message', index=6,
-      number=6, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='no_unions', full_name='NanoPBOptions.no_unions', index=7,
-      number=8, type=8, cpp_type=7, label=1,
-      has_default_value=True, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='msgid', full_name='NanoPBOptions.msgid', index=8,
-      number=9, type=13, cpp_type=3, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=51,
-  serialized_end=307,
-)
-
-_NANOPBOPTIONS.fields_by_name['int_size'].enum_type = _INTSIZE
-_NANOPBOPTIONS.fields_by_name['type'].enum_type = _FIELDTYPE
-DESCRIPTOR.message_types_by_name['NanoPBOptions'] = _NANOPBOPTIONS
-DESCRIPTOR.enum_types_by_name['FieldType'] = _FIELDTYPE
-DESCRIPTOR.enum_types_by_name['IntSize'] = _INTSIZE
-DESCRIPTOR.extensions_by_name['nanopb_fileopt'] = nanopb_fileopt
-DESCRIPTOR.extensions_by_name['nanopb_msgopt'] = nanopb_msgopt
-DESCRIPTOR.extensions_by_name['nanopb_enumopt'] = nanopb_enumopt
-DESCRIPTOR.extensions_by_name['nanopb'] = nanopb
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-NanoPBOptions = _reflection.GeneratedProtocolMessageType('NanoPBOptions', (_message.Message,), dict(
-  DESCRIPTOR = _NANOPBOPTIONS,
-  __module__ = 'nanopb_pb2'
-  # @@protoc_insertion_point(class_scope:NanoPBOptions)
-  ))
-_sym_db.RegisterMessage(NanoPBOptions)
-
-nanopb_fileopt.message_type = _NANOPBOPTIONS
-google_dot_protobuf_dot_descriptor__pb2.FileOptions.RegisterExtension(nanopb_fileopt)
-nanopb_msgopt.message_type = _NANOPBOPTIONS
-google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(nanopb_msgopt)
-nanopb_enumopt.message_type = _NANOPBOPTIONS
-google_dot_protobuf_dot_descriptor__pb2.EnumOptions.RegisterExtension(nanopb_enumopt)
-nanopb.message_type = _NANOPBOPTIONS
-google_dot_protobuf_dot_descriptor__pb2.FieldOptions.RegisterExtension(nanopb)
-
-DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\030fi.kapsi.koti.jpa.nanopb'))
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\030fi.kapsi.koti.jpa.nanopb'
+  _FIELDTYPE._serialized_start=309
+  _FIELDTYPE._serialized_end=399
+  _INTSIZE._serialized_start=401
+  _INTSIZE._serialized_end=469
+  _NANOPBOPTIONS._serialized_start=51
+  _NANOPBOPTIONS._serialized_end=307
 # @@protoc_insertion_point(module_scope)
diff --git a/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/plugin_pb2.py b/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/plugin_pb2.py
index d693863..79fffcd 100644
--- a/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/plugin_pb2.py
+++ b/acts/framework/acts/controllers/buds_lib/dev_utils/proto/gen/plugin_pb2.py
@@ -1,13 +1,11 @@
+# -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: plugin.proto
-
-import sys
-_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
-from google.protobuf import descriptor_pb2
 # @@protoc_insertion_point(imports)
 
 _sym_db = _symbol_database.Default()
@@ -16,173 +14,18 @@
 from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='plugin.proto',
-  package='google.protobuf.compiler',
-  syntax='proto2',
-  serialized_pb=_b('\n\x0cplugin.proto\x12\x18google.protobuf.compiler\x1a google/protobuf/descriptor.proto\"}\n\x14\x43odeGeneratorRequest\x12\x18\n\x10\x66ile_to_generate\x18\x01 \x03(\t\x12\x11\n\tparameter\x18\x02 \x01(\t\x12\x38\n\nproto_file\x18\x0f \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\"\xaa\x01\n\x15\x43odeGeneratorResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\x12\x42\n\x04\x66ile\x18\x0f \x03(\x0b\x32\x34.google.protobuf.compiler.CodeGeneratorResponse.File\x1a>\n\x04\x46ile\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x17\n\x0finsertion_point\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x0f \x01(\tB,\n\x1c\x63om.google.protobuf.compilerB\x0cPluginProtos')
-  ,
-  dependencies=[google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,])
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cplugin.proto\x12\x18google.protobuf.compiler\x1a google/protobuf/descriptor.proto\"}\n\x14\x43odeGeneratorRequest\x12\x18\n\x10\x66ile_to_generate\x18\x01 \x03(\t\x12\x11\n\tparameter\x18\x02 \x01(\t\x12\x38\n\nproto_file\x18\x0f \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\"\xaa\x01\n\x15\x43odeGeneratorResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\x12\x42\n\x04\x66ile\x18\x0f \x03(\x0b\x32\x34.google.protobuf.compiler.CodeGeneratorResponse.File\x1a>\n\x04\x46ile\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x17\n\x0finsertion_point\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x0f \x01(\tB,\n\x1c\x63om.google.protobuf.compilerB\x0cPluginProtos')
 
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'plugin_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-
-
-_CODEGENERATORREQUEST = _descriptor.Descriptor(
-  name='CodeGeneratorRequest',
-  full_name='google.protobuf.compiler.CodeGeneratorRequest',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='file_to_generate', full_name='google.protobuf.compiler.CodeGeneratorRequest.file_to_generate', index=0,
-      number=1, type=9, cpp_type=9, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='parameter', full_name='google.protobuf.compiler.CodeGeneratorRequest.parameter', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='proto_file', full_name='google.protobuf.compiler.CodeGeneratorRequest.proto_file', index=2,
-      number=15, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=76,
-  serialized_end=201,
-)
-
-
-_CODEGENERATORRESPONSE_FILE = _descriptor.Descriptor(
-  name='File',
-  full_name='google.protobuf.compiler.CodeGeneratorResponse.File',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='google.protobuf.compiler.CodeGeneratorResponse.File.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='insertion_point', full_name='google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='content', full_name='google.protobuf.compiler.CodeGeneratorResponse.File.content', index=2,
-      number=15, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=312,
-  serialized_end=374,
-)
-
-_CODEGENERATORRESPONSE = _descriptor.Descriptor(
-  name='CodeGeneratorResponse',
-  full_name='google.protobuf.compiler.CodeGeneratorResponse',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='error', full_name='google.protobuf.compiler.CodeGeneratorResponse.error', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-    _descriptor.FieldDescriptor(
-      name='file', full_name='google.protobuf.compiler.CodeGeneratorResponse.file', index=1,
-      number=15, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      options=None),
-  ],
-  extensions=[
-  ],
-  nested_types=[_CODEGENERATORRESPONSE_FILE, ],
-  enum_types=[
-  ],
-  options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=204,
-  serialized_end=374,
-)
-
-_CODEGENERATORREQUEST.fields_by_name['proto_file'].message_type = google_dot_protobuf_dot_descriptor__pb2._FILEDESCRIPTORPROTO
-_CODEGENERATORRESPONSE_FILE.containing_type = _CODEGENERATORRESPONSE
-_CODEGENERATORRESPONSE.fields_by_name['file'].message_type = _CODEGENERATORRESPONSE_FILE
-DESCRIPTOR.message_types_by_name['CodeGeneratorRequest'] = _CODEGENERATORREQUEST
-DESCRIPTOR.message_types_by_name['CodeGeneratorResponse'] = _CODEGENERATORRESPONSE
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-CodeGeneratorRequest = _reflection.GeneratedProtocolMessageType('CodeGeneratorRequest', (_message.Message,), dict(
-  DESCRIPTOR = _CODEGENERATORREQUEST,
-  __module__ = 'plugin_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorRequest)
-  ))
-_sym_db.RegisterMessage(CodeGeneratorRequest)
-
-CodeGeneratorResponse = _reflection.GeneratedProtocolMessageType('CodeGeneratorResponse', (_message.Message,), dict(
-
-  File = _reflection.GeneratedProtocolMessageType('File', (_message.Message,), dict(
-    DESCRIPTOR = _CODEGENERATORRESPONSE_FILE,
-    __module__ = 'plugin_pb2'
-    # @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorResponse.File)
-    ))
-  ,
-  DESCRIPTOR = _CODEGENERATORRESPONSE,
-  __module__ = 'plugin_pb2'
-  # @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorResponse)
-  ))
-_sym_db.RegisterMessage(CodeGeneratorResponse)
-_sym_db.RegisterMessage(CodeGeneratorResponse.File)
-
-
-DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\034com.google.protobuf.compilerB\014PluginProtos'))
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\034com.google.protobuf.compilerB\014PluginProtos'
+  _CODEGENERATORREQUEST._serialized_start=76
+  _CODEGENERATORREQUEST._serialized_end=201
+  _CODEGENERATORRESPONSE._serialized_start=204
+  _CODEGENERATORRESPONSE._serialized_end=374
+  _CODEGENERATORRESPONSE_FILE._serialized_start=312
+  _CODEGENERATORRESPONSE_FILE._serialized_end=374
 # @@protoc_insertion_point(module_scope)
diff --git a/acts/framework/acts/controllers/buds_lib/tako_trace_logger.py b/acts/framework/acts/controllers/buds_lib/tako_trace_logger.py
index ff31840..a9d8afe 100644
--- a/acts/framework/acts/controllers/buds_lib/tako_trace_logger.py
+++ b/acts/framework/acts/controllers/buds_lib/tako_trace_logger.py
@@ -53,4 +53,3 @@
         Note that flushing the log is handled automatically by python's logging
         module.
         """
-        pass
diff --git a/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py b/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py
index be3056a..0e2d86f 100644
--- a/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py
+++ b/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py
@@ -16,8 +16,15 @@
 
 from acts.controllers.android_lib.tel import tel_utils
 from acts.controllers.cellular_lib import BaseCellularDut
+import os
+import time
 
 GET_BUILD_VERSION = 'getprop ro.build.version.release'
+PIXELLOGGER_CONTROL = 'am broadcast -n com.android.pixellogger/.receiver.' \
+                      'AlwaysOnLoggingReceiver -a com.android.pixellogger.' \
+                      'service.logging.LoggingService.' \
+                      'ACTION_CONFIGURE_ALWAYS_ON_LOGGING ' \
+                      '-e intent_key_enable "{}"'
 
 NETWORK_TYPE_TO_BITMASK = {
     BaseCellularDut.PreferredNetworkType.LTE_ONLY: '01000001000000000000',
@@ -36,6 +43,8 @@
         """
         self.ad = ad
         self.log = logger
+        logger.info('Initializing Android DUT with baseband version {}'.format(
+            ad.adb.getprop('gsm.version.baseband')))
 
     def toggle_airplane_mode(self, new_state=True):
         """ Turns airplane mode on / off.
@@ -80,15 +89,18 @@
           type: an instance of class PreferredNetworkType
         """
 
+        self.log.info('setting preferred network type: {}'.format(type))
         # If android version is S or later, uses bit mask to set and return.
         version = self.ad.adb.shell(GET_BUILD_VERSION)
+        self.log.info('The android version is {}'.format(version))
         try:
             version_in_number = int(version)
             if version_in_number > 11:
-                set_network_cmd = 'cmd phone set-allowed-network-types-for-users '
-                set_network_cmd += NETWORK_TYPE_TO_BITMASK[type]
+                base_cmd = 'cmd phone set-allowed-network-types-for-users -s 0 '
+                set_network_cmd = base_cmd + NETWORK_TYPE_TO_BITMASK[type]
                 self.ad.adb.shell(set_network_cmd)
-                get_network_cmd = 'cmd phone get-allowed-network-types-for-users'
+                get_network_cmd = ('cmd phone '
+                                   'get-allowed-network-types-for-users -s 0')
                 allowed_network = self.ad.adb.shell(get_network_cmd)
                 self.log.info('The allowed network: {}'.format(allowed_network))
                 return
@@ -115,3 +127,33 @@
 
         Will be deprecated and replaced by get_rx_tx_power_levels. """
         tel_utils.get_telephony_signal_strength(self.ad)
+
+    def start_modem_logging(self):
+        """ Starts on-device log collection. """
+        self.ad.adb.shell('rm /data/vendor/slog/*.* -f')
+        self.ad.adb.shell(PIXELLOGGER_CONTROL.format('true'))
+
+    def stop_modem_logging(self):
+        """ Stops log collection and pulls logs. """
+        output_path = self.ad.device_log_path + '/modem/'
+        os.makedirs(output_path, exist_ok=True)
+        self.ad.adb.shell(PIXELLOGGER_CONTROL.format('false'))
+
+    def toggle_data(self, new_state=True):
+        """ Turns on/off of the cellular data.
+
+        Args:
+            new_state: True to enable cellular data
+        """
+        self.log.info('Toggles cellular data on: {}'.format(new_state))
+        if new_state:
+            self.ad.adb.shell('settings put global mobile_data 1')
+            self.ad.adb.shell('svc data enable')
+            time.sleep(5)
+            self.log.info('global mobile data: {}'.format(
+                self.ad.adb.shell('settings get global mobile_data')))
+        else:
+            self.ad.adb.shell('settings put global mobile_data 0')
+            self.ad.adb.shell('svc data disable')
+            self.log.info('global mobile data: {}'.format(
+                self.ad.adb.shell('settings get global mobile_data')))
diff --git a/acts/framework/acts/controllers/cellular_lib/BaseCellConfig.py b/acts/framework/acts/controllers/cellular_lib/BaseCellConfig.py
index 14540de..9869264 100644
--- a/acts/framework/acts/controllers/cellular_lib/BaseCellConfig.py
+++ b/acts/framework/acts/controllers/cellular_lib/BaseCellConfig.py
@@ -46,5 +46,5 @@
             new_config: 5G cell configuration object.
         """
         for attr, value in vars(new_config).items():
-            if value and not hasattr(self, attr):
+            if value is not None:
                 setattr(self, attr, value)
diff --git a/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py b/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py
index 5bdbc1c..0bf6170 100644
--- a/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py
+++ b/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py
@@ -77,3 +77,11 @@
 
         Will be deprecated and replaced by get_rx_tx_power_levels. """
         raise NotImplementedError()
+
+    def start_modem_logging(self):
+        """ Starts on-device log collection. """
+        raise NotImplementedError()
+
+    def stop_modem_logging(self):
+        """ Stops log collection and pulls logs. """
+        raise NotImplementedError()
\ No newline at end of file
diff --git a/acts/framework/acts/controllers/cellular_lib/BaseSimulation.py b/acts/framework/acts/controllers/cellular_lib/BaseSimulation.py
index 5e53786..8332584 100644
--- a/acts/framework/acts/controllers/cellular_lib/BaseSimulation.py
+++ b/acts/framework/acts/controllers/cellular_lib/BaseSimulation.py
@@ -68,9 +68,13 @@
     # the simulations inheriting from this class.
     DOWNLINK_SIGNAL_LEVEL_UNITS = None
 
-    def __init__(
-        self, simulator, log, dut, test_config, calibration_table,
-        nr_mode=None):
+    def __init__(self,
+                 simulator,
+                 log,
+                 dut,
+                 test_config,
+                 calibration_table,
+                 nr_mode=None):
         """ Initializes the Simulation object.
 
         Keeps a reference to the callbox, log and dut handlers and
@@ -739,3 +743,11 @@
             Maximum throughput in mbps
         """
         raise NotImplementedError()
+
+    def send_sms(self, message):
+        """ Sends an SMS message to the DUT.
+
+        Args:
+            message: the SMS message to send.
+        """
+        raise NotImplementedError()
diff --git a/acts/framework/acts/controllers/cellular_lib/LteCellConfig.py b/acts/framework/acts/controllers/cellular_lib/LteCellConfig.py
index 34b982d..6d47087 100644
--- a/acts/framework/acts/controllers/cellular_lib/LteCellConfig.py
+++ b/acts/framework/acts/controllers/cellular_lib/LteCellConfig.py
@@ -113,6 +113,9 @@
         self.drx_long_cycle = None
         self.drx_long_cycle_offset = None
 
+    def __str__(self):
+        return str(vars(self))
+
     def configure(self, parameters):
         """ Configures an LTE cell using a dictionary of parameters.
 
diff --git a/acts/framework/acts/controllers/cellular_lib/LteImsSimulation.py b/acts/framework/acts/controllers/cellular_lib/LteImsSimulation.py
index a47988e..e011478 100644
--- a/acts/framework/acts/controllers/cellular_lib/LteImsSimulation.py
+++ b/acts/framework/acts/controllers/cellular_lib/LteImsSimulation.py
@@ -43,7 +43,9 @@
         # Make sure the IMS registration was successful by verifying the CSCF
         # status is SIP IDLE.
         if not _wait_for_ims_cscf_status(
-                self.log, self.anritsu, DEFAULT_IMS_VIRTUAL_NETWORK_ID,
+                self.log,
+                self.simulator.anritsu,
+                DEFAULT_IMS_VIRTUAL_NETWORK_ID,
                 md8475a.ImsCscfStatus.SIPIDLE.value):
             self.log.error('UE failed to register with the IMS server.')
             return False
diff --git a/acts/framework/acts/controllers/cellular_lib/LteSimulation.py b/acts/framework/acts/controllers/cellular_lib/LteSimulation.py
index 346046c..250a0d1 100644
--- a/acts/framework/acts/controllers/cellular_lib/LteSimulation.py
+++ b/acts/framework/acts/controllers/cellular_lib/LteSimulation.py
@@ -22,6 +22,13 @@
 from acts.controllers.cellular_lib import BaseCellularDut
 
 
+class IPAddressType(Enum):
+    """ IP Address types"""
+    IPV4 = "IPV4"
+    IPV6 = "IPV6"
+    IPV4V6 = "IPV4V6"
+
+
 class TransmissionMode(Enum):
     """ Transmission modes for LTE (e.g., TM1, TM4, ...) """
     TM1 = "TM1"
@@ -87,7 +94,7 @@
     # RSRP signal levels thresholds (as reported by Android) in dBm/15KHz.
     # Excellent is set to -75 since callbox B Tx power is limited to -30 dBm
     DOWNLINK_SIGNAL_LEVEL_DICTIONARY = {
-        'excellent': -75,
+        'excellent': -62,
         'high': -110,
         'medium': -115,
         'weak': -120,
@@ -411,9 +418,13 @@
         tdd_config4_tput_lut  # DL 256QAM, UL 64 QAM OFF & MAC padding ON
     }
 
-    def __init__(
-        self, simulator, log, dut, test_config, calibration_table,
-        nr_mode=None):
+    def __init__(self,
+                 simulator,
+                 log,
+                 dut,
+                 test_config,
+                 calibration_table,
+                 nr_mode=None):
         """ Initializes the simulator for a single-carrier LTE simulation.
 
         Args:
@@ -426,8 +437,8 @@
 
         """
 
-        super().__init__(
-            simulator, log, dut, test_config, calibration_table, nr_mode)
+        super().__init__(simulator, log, dut, test_config, calibration_table,
+                         nr_mode)
 
         self.num_carriers = None
 
@@ -435,14 +446,14 @@
         try:
             if self.nr_mode and 'nr' == self.nr_mode:
                 self.dut.set_preferred_network_type(
-                    BaseCellularDut.PreferredNetworkType.LTE_NR)
+                    BaseCellularDut.PreferredNetworkType.NR_LTE)
             else:
                 self.dut.set_preferred_network_type(
                     BaseCellularDut.PreferredNetworkType.LTE_ONLY)
         except Exception as e:
             # If this fails the test should be able to run anyways, even if it
             # takes longer to find the cell.
-            self.log.warning('Setting preferred RAT failed: ' + str(e))
+            self.log.warning('Setting preferred RAT failed: {}'.format(e))
 
         # Get LTE CA frequency bands setting from the test configuration
         if self.KEY_FREQ_BANDS not in test_config:
@@ -512,17 +523,32 @@
                     new_cell_list.append(dict(cell))
                     bw = int(cell[LteCellConfig.PARAM_BW])
                     dl_earfcn = LteCellConfig.PARAM_DL_EARFCN
-                    new_cell_list[-1][dl_earfcn] = self.LOWEST_DL_CN_DICTIONARY[
-                        int(band_num)] + bw * 10 - 2
+                    new_cell_list[-1][
+                        dl_earfcn] = self.LOWEST_DL_CN_DICTIONARY[int(
+                            band_num)] + bw * 10 - 2
             else:
                 # The band is just a number, so just add it to the list
                 new_cell_list.append(cell)
 
+        # verify mimo mode parameter is provided
+        for cell in new_cell_list:
+            if not LteCellConfig.PARAM_MIMO in cell:
+                raise ValueError(
+                    'The config dictionary must include parameter "{}" with the'
+                    ' mimo mode.'.format(self.PARAM_MIMO))
+
+            if cell[LteCellConfig.PARAM_MIMO] not in (m.value
+                                                      for m in MimoMode):
+                raise ValueError(
+                    'The value of {} must be one of the following:'
+                    '1x1, 2x2 or 4x4.'.format(self.PARAM_MIMO))
+
         # Logs new_cell_list for debug
         self.log.info('new cell list: {}'.format(new_cell_list))
 
         self.simulator.set_band_combination(
-            [c[LteCellConfig.PARAM_BAND] for c in new_cell_list])
+            [c[LteCellConfig.PARAM_BAND] for c in new_cell_list],
+            [MimoMode(c[LteCellConfig.PARAM_MIMO]) for c in new_cell_list])
 
         self.num_carriers = len(new_cell_list)
 
@@ -604,7 +630,7 @@
 
         bandwidth = bts_config.bandwidth
 
-        if bandwidth == 100: # This assumes 273 RBs. TODO: b/229163022
+        if bandwidth == 100:  # This assumes 273 RBs. TODO: b/229163022
             power = rsrp + 35.15
         elif bandwidth == 20:  # 100 RBs
             power = rsrp + 30.79
@@ -921,3 +947,11 @@
                     self.cell_configs[bts_index].incorporate(new_config)
 
             self.simulator.lte_attach_secondary_carriers(self.freq_bands)
+
+    def send_sms(self, message):
+        """ Sends an SMS message to the DUT.
+
+        Args:
+            message: the SMS message to send.
+        """
+        self.simulator.send_sms(message)
diff --git a/acts/framework/acts/controllers/cellular_lib/NrCellConfig.py b/acts/framework/acts/controllers/cellular_lib/NrCellConfig.py
index 5a18025..7eef6fb 100644
--- a/acts/framework/acts/controllers/cellular_lib/NrCellConfig.py
+++ b/acts/framework/acts/controllers/cellular_lib/NrCellConfig.py
@@ -15,6 +15,7 @@
 #   limitations under the License.
 
 import acts.controllers.cellular_lib.BaseCellConfig as base_cell
+import acts.controllers.cellular_lib.LteSimulation as lte_sim
 
 
 class NrCellConfig(base_cell.BaseCellConfig):
@@ -25,8 +26,18 @@
         bandwidth: a integer indicating the required channel bandwidth
     """
 
-    PARAM_BAND = "band"
-    PARAM_BW = "bw"
+    PARAM_BAND = 'band'
+    PARAM_BW = 'bw'
+    PARAM_DL_MCS = 'dlmcs'
+    PARAM_DL_RBS = 'dl_rbs'
+    PARAM_PADDING = 'mac_padding'
+    PARAM_MIMO = 'mimo'
+    PARAM_NRARFCN = 'nr_arfcn'
+    PARAM_SCHEDULING = "scheduling"
+    PARAM_SCHEDULING_DYNAMIC = "dynamic"
+    PARAM_SCHEDULING_STATIC = "static"
+    PARAM_UL_MCS = 'ulmcs'
+    PARAM_UL_RBS = 'ul_rbs'
 
     def __init__(self, log):
         """ Initialize the base station config by setting all its
@@ -37,6 +48,13 @@
         super().__init__(log)
         self.band = None
         self.bandwidth = None
+        self.dl_rbs = None
+        self.ul_rbs = None
+        self.dl_mcs = None
+        self.ul_mcs = None
+        self.mac_padding = None
+        self.mimo_mode = None
+        self.nr_arfcn = None
 
     def configure(self, parameters):
         """ Configures an NR cell using a dictionary of parameters.
@@ -48,13 +66,72 @@
             raise ValueError(
                 "The configuration dictionary must include a key '{}' with "
                 "the required band number.".format(self.PARAM_BAND))
+        nr_band = parameters[self.PARAM_BAND]
+        if nr_band[0] == 'n':
+            nr_band = nr_band[1:]
+        self.band = nr_band
 
-        self.band = parameters[self.PARAM_BAND]
+        if self.PARAM_NRARFCN in parameters:
+            self.nr_arfcn = int(parameters[self.PARAM_NRARFCN])
 
         if self.PARAM_BW not in parameters:
             raise ValueError(
                 "The config dictionary must include parameter {} with an "
                 "int value (to indicate 1.4 MHz use 14).".format(
                     self.PARAM_BW))
+        bw = float(parameters[self.PARAM_BW])
 
-        self.bandwidth = parameters[self.PARAM_BW]
+        if abs(bw - 14) < 0.00000000001:
+            bw = 1.4
+
+        self.bandwidth = bw
+
+        # Setup mimo mode
+        if self.PARAM_MIMO not in parameters:
+            raise ValueError(
+                "The config dictionary must include parameter '{}' with the "
+                "mimo mode.".format(self.PARAM_MIMO))
+
+        for mimo_mode in lte_sim.MimoMode:
+            if parameters[self.PARAM_MIMO] == mimo_mode.value:
+                self.mimo_mode = mimo_mode
+                break
+        else:
+            raise ValueError("The value of {} must be one of the following:"
+                             "1x1, 2x2 or 4x4.".format(self.PARAM_MIMO))
+
+        if self.PARAM_SCHEDULING not in parameters:
+            self.scheduling_mode = lte_sim.SchedulingMode.STATIC
+            self.log.warning(
+                "The test config does not include the '{}' key. Setting to "
+                "static by default.".format(self.PARAM_SCHEDULING))
+        elif parameters[
+                self.PARAM_SCHEDULING] == self.PARAM_SCHEDULING_DYNAMIC:
+            self.scheduling_mode = lte_sim.SchedulingMode.DYNAMIC
+        elif parameters[self.PARAM_SCHEDULING] == self.PARAM_SCHEDULING_STATIC:
+            self.scheduling_mode = lte_sim.SchedulingMode.STATIC
+        else:
+            raise ValueError("Key '{}' must have a value of "
+                             "'dynamic' or 'static'.".format(
+                                 self.PARAM_SCHEDULING))
+
+        if self.scheduling_mode == lte_sim.SchedulingMode.STATIC:
+
+            if self.PARAM_PADDING not in parameters:
+                self.log.warning(
+                    "The '{}' parameter was not set. Enabling MAC padding by "
+                    "default.".format(self.PARAM_PADDING))
+                self.mac_padding = True
+
+            if self.PARAM_DL_MCS in parameters:
+                self.dl_mcs = int(parameters[self.PARAM_DL_MCS])
+
+            if self.PARAM_UL_MCS in parameters:
+                self.ul_mcs = int(parameters[self.PARAM_UL_MCS])
+
+            # Temproraily setting: set 273 for bandwidth of 100 MHz
+            self.dl_rbs = 273
+            self.ul_rbs = 273
+
+    def __str__(self):
+        return str(vars(self))
diff --git a/acts/framework/acts/controllers/cellular_lib/OWNERS b/acts/framework/acts/controllers/cellular_lib/OWNERS
index e4010df..f88a96c 100644
--- a/acts/framework/acts/controllers/cellular_lib/OWNERS
+++ b/acts/framework/acts/controllers/cellular_lib/OWNERS
@@ -1,4 +1,8 @@
 iguarna@google.com
 chaoyangf@google.com
 yixiang@google.com
-codycaldwell@google.com
\ No newline at end of file
+codycaldwell@google.com
+
+per-file PresetSimulation.py = hmtuan@google.com
+per-file PresetSimulation.py = harjani@google.com
+per-file PresetSimulation.py = jethier@google.com
\ No newline at end of file
diff --git a/acts/framework/acts/controllers/cellular_lib/PresetSimulation.py b/acts/framework/acts/controllers/cellular_lib/PresetSimulation.py
new file mode 100644
index 0000000..d536b09
--- /dev/null
+++ b/acts/framework/acts/controllers/cellular_lib/PresetSimulation.py
@@ -0,0 +1,245 @@
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts.controllers.cellular_lib.BaseSimulation import BaseSimulation
+from acts.controllers.cellular_lib import BaseCellularDut
+
+
+class PresetSimulation(BaseSimulation):
+    """5G preset simulation.
+
+    The simulation will be configed by importing SCPI config file
+    instead of individually set params.
+    """
+
+    # Keys to obtain settings from the test_config dictionary.
+    KEY_CELL_INFO = "cell_info"
+    KEY_SCPI_FILE_NAME = "scpi_file"
+
+    NETWORK_BIT_MASK = {
+        'nr_lte': '11000001000000000000'
+    }
+    ADB_CMD_LOCK_NETWORK = 'cmd phone set-allowed-network-types-for-users -s 0 {network_bit_mask}'
+    NR_LTE_BIT_MASK_KEY = 'nr_lte'
+
+    def __init__(self,
+                 simulator,
+                 log,
+                 dut,
+                 test_config,
+                 calibration_table,
+                 nr_mode=None):
+        """Initializes the simulator for 5G preset simulation.
+
+        Args:
+            simulator: a cellular simulator controller.
+            log: a logger handle.
+            dut: a device handler implementing BaseCellularDut.
+            test_config: test configuration obtained from the config file.
+            calibration_table: a dictionary containing path losses
+                for different bands.
+        """
+
+        super().__init__(simulator, log, dut, test_config, calibration_table,
+                         nr_mode)
+        # require param for idle test case
+        self.rrc_sc_timer = 0
+
+        # Set to KeySight APN
+        log.info('Configuring APN.')
+        self.dut.set_apn('Keysight', 'Keysight')
+        self.num_carriers = None
+
+        # Enable roaming on the phone
+        self.dut.toggle_data_roaming(True)
+
+    def setup_simulator(self):
+        """Do initial configuration in the simulator. """
+        self.log.info('This simulation does not require initial setup.')
+
+    def configure(self, parameters):
+        """Configures simulation by importing scpi file.
+
+        A pre-made SCPI file include all the essential configuration
+        for the simulation is imported by send SCPI import command
+        to the callbox.
+
+        Args:
+            parameters: a configuration dictionary which includes scpi file path
+                if there is only one carrier, a list if there are multiple cells.
+        """
+        scpi_file = parameters[0][self.KEY_SCPI_FILE_NAME]
+        cell_infos = parameters[0][self.KEY_CELL_INFO]
+
+        self.log.info('Configure test scenario with\n' +
+                      f' SCPI config file: {scpi_file}\n' +
+                      f' cell info: {cell_infos}')
+
+        self.simulator.import_configuration(scpi_file)
+        self.simulator.set_cell_info(cell_infos)
+
+    def start(self):
+        """Start simulation.
+
+        Waiting for the DUT to connect to the callbox.
+
+        Raise:
+            RuntimeError: simulation fail to start
+                due to unable to connect dut and cells.
+        """
+        self.attach()
+
+    def attach(self):
+        """Attach UE to the callbox.
+
+        Toggle airplane mode on-off and wait for a specified timeout,
+        repeat until the UE connect to the callbox.
+
+        Raise:
+            RuntimeError: attaching fail
+                due to unable to connect dut and cells.
+        """
+        try:
+            self.simulator.wait_until_attached(self.dut, self.attach_timeout,
+                                               self.attach_retries)
+        except Exception as exc:
+            raise RuntimeError('Could not attach to base station.') from exc
+
+    def calibrated_downlink_rx_power(self, bts_config, rsrp):
+        """Convert RSRP to total signal power from the basestation.
+
+        Args:
+            bts_config: the current configuration at the base station
+            rsrp: desired rsrp, contained in a key value pair
+        """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
+
+    def downlink_calibration(self, rat=None, power_units_conversion_func=None):
+        """Computes downlink path loss and returns the calibration value.
+
+        See base class implementation for details.
+
+        Args:
+            rat: ignored, replaced by 'lteRsrp'.
+            power_units_conversion_func: ignored, replaced by
+                self.rsrp_to_signal_power.
+
+        Returns:
+            Downlink calibration value and measured DL power. Note that the
+            phone only reports RSRP of the primary chain
+        """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
+
+    def rsrp_to_signal_power(self, rsrp, bts_config):
+        """Converts rsrp to total band signal power
+
+        RSRP is measured per subcarrier, so total band power needs to be
+        multiplied by the number of subcarriers being used.
+
+        Args:
+            rsrp: desired rsrp in dBm.
+            bts_config: a base station configuration object.
+
+        Returns:
+            Total band signal power in dBm
+        """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
+
+    def maximum_downlink_throughput(self):
+        """Calculates maximum achievable downlink throughput in.
+
+        The calculation is based on the current simulation state
+        Returns:
+            Maximum throughput in mbps.
+        """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
+
+    def bts_maximum_downlink_throughtput(self, bts_config):
+        """Calculates maximum achievable downlink throughput for a single
+
+        base station from its configuration object.
+
+        Args:
+            bts_config: a base station configuration object.
+
+        Returns:
+            Maximum throughput in mbps.
+        """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
+
+    def maximum_uplink_throughput(self):
+        """Calculates maximum achievable uplink throughput.
+
+        Returns:
+            Maximum throughput in mbps.
+        """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
+
+    def bts_maximum_uplink_throughtput(self, bts_config):
+        """Calculates maximum achievable uplink throughput
+
+        The calculation is for selected basestation
+        from its configuration object.
+        Args:
+            bts_config: an LTE base station configuration object.
+
+        Returns:
+            Maximum throughput in mbps.
+
+        """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
+
+    def calibrate(self, band):
+        """Calculates UL and DL path loss if it wasn't done before
+
+        Before running the base class implementation, configure the base station
+        to only use one downlink antenna with maximum bandwidth.
+
+        Args:
+            band: the band that is currently being calibrated.
+        """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
+
+    def start_traffic_for_calibration(self):
+        """If MAC padding is enabled, there is no need to start IP traffic. """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
+
+    def stop_traffic_for_calibration(self):
+        """If MAC padding is enabled, IP traffic wasn't started. """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
+
+    def get_measured_ul_power(self, samples=5, wait_after_sample=3):
+        """Calculates UL power.
+
+        The calculation is based on measurements from the callbox
+        and the calibration data.
+        Args:
+            samples: the numble of samples to average
+            wait_after_sample: time in seconds to wait in between samples
+
+        Returns:
+            the ul power at the UE antenna ports in dBs
+        """
+        raise NotImplementedError(
+            'This simulation mode does not support this configuration option')
diff --git a/acts/framework/acts/controllers/cellular_simulator.py b/acts/framework/acts/controllers/cellular_simulator.py
index 0a026e6..471b14a 100644
--- a/acts/framework/acts/controllers/cellular_simulator.py
+++ b/acts/framework/acts/controllers/cellular_simulator.py
@@ -45,11 +45,12 @@
         """ Configures the equipment for an LTE simulation. """
         raise NotImplementedError()
 
-    def set_band_combination(self, bands):
-        """ Prepares the test equipment for the indicated CA combination.
+    def set_band_combination(self, bands, mimo_modes):
+        """ Prepares the test equipment for the indicated band/mimo combination.
 
         Args:
             bands: a list of bands represented as ints or strings
+            mimo_modes: a list of LteSimulation.MimoMode to use for each antenna
         """
         raise NotImplementedError()
 
@@ -63,6 +64,13 @@
             bts_index: the base station number.
         """
 
+        config_vars = vars(config)
+        config_dict = {
+            key: config_vars[key]
+            for key in config_vars if config_vars[key]
+        }
+        self.log.info('The config for {} is {}'.format(bts_index, config_dict))
+
         if config.output_power:
             self.set_output_power(bts_index, config.output_power)
 
@@ -72,6 +80,9 @@
         if isinstance(config, cellular_lib.LteCellConfig.LteCellConfig):
             self.configure_lte_bts(config, bts_index)
 
+        if isinstance(config, cellular_lib.NrCellConfig.NrCellConfig):
+            self.configure_nr_bts(config, bts_index)
+
     def configure_lte_bts(self, config, bts_index=0):
         """ Commands the equipment to setup an LTE base station with the
         required configuration.
@@ -111,8 +122,8 @@
 
         if config.scheduling_mode:
 
-            if (config.scheduling_mode ==
-                    cellular_lib.LteSimulation.SchedulingMode.STATIC
+            if (config.scheduling_mode
+                    == cellular_lib.LteSimulation.SchedulingMode.STATIC
                     and not (config.dl_rbs and config.ul_rbs and config.dl_mcs
                              and config.ul_mcs)):
                 raise ValueError('When the scheduling mode is set to manual, '
@@ -160,6 +171,43 @@
                 self.set_drx_long_cycle_offset(bts_index,
                                                config.drx_long_cycle_offset)
 
+    def configure_nr_bts(self, config, bts_index=1):
+        """ Commands the equipment to setup an LTE base station with the
+        required configuration.
+
+        Args:
+            config: an LteSimulation.BtsConfig object.
+            bts_index: the base station number.
+        """
+        if config.band:
+            self.set_band(bts_index, config.band)
+
+        if config.nr_arfcn:
+            self.set_downlink_channel_number(bts_index, config.nr_arfcn)
+
+        if config.bandwidth:
+            self.set_bandwidth(bts_index, config.bandwidth)
+
+        if config.mimo_mode:
+            self.set_mimo_mode(bts_index, config.mimo_mode)
+
+        if config.scheduling_mode:
+
+            if (config.scheduling_mode
+                    == cellular_lib.LteSimulation.SchedulingMode.STATIC
+                    and not (config.dl_rbs and config.ul_rbs and config.dl_mcs
+                             and config.ul_mcs)):
+                raise ValueError('When the scheduling mode is set to manual, '
+                                 'the RB and MCS parameters are required.')
+
+            # If scheduling mode is set to Dynamic, the RB and MCS parameters
+            # will be ignored by set_scheduling_mode.
+            self.set_scheduling_mode(bts_index, config.scheduling_mode,
+                                     config.dl_mcs, config.ul_mcs,
+                                     config.dl_rbs, config.ul_rbs)
+        if config.mac_padding is not None:
+            self.set_mac_padding(bts_index, config.mac_padding)
+
     def set_lte_rrc_state_change_timer(self, enabled, time=10):
         """ Configures the LTE RRC state change timer.
 
@@ -380,6 +428,30 @@
         """
         raise NotImplementedError()
 
+    def set_apn(self, apn):
+        """ Configures the callbox network Access Point Name.
+
+        Args:
+            apn: the APN name
+        """
+        raise NotImplementedError()
+
+    def set_ip_type(self, ip_type):
+        """ Configures the callbox network IP type.
+
+        Args:
+            ip_type: the network type to use.
+        """
+        raise NotImplementedError()
+
+    def set_mtu(self, mtu):
+        """ Configures the callbox network Maximum Transmission Unit.
+
+        Args:
+            mtu: the MTU size.
+        """
+        raise NotImplementedError()
+
     def lte_attach_secondary_carriers(self, ue_capability_enquiry):
         """ Activates the secondary carriers for CA. Requires the DUT to be
         attached to the primary carrier first.
@@ -417,6 +489,15 @@
         """
         raise NotImplementedError()
 
+    def wait_until_quiet(self, timeout=120):
+        """Waits for all pending operations to finish on the simulator.
+
+        Args:
+            timeout: after this amount of time the method will raise a
+                CellularSimulatorError exception. Default is 120 seconds.
+        """
+        raise NotImplementedError()
+
     def detach(self):
         """ Turns off all the base stations so the DUT loose connection."""
         raise NotImplementedError()
@@ -442,8 +523,15 @@
         """
         raise NotImplementedError()
 
+    def send_sms(self, message):
+        """ Sends an SMS message to the DUT.
+
+        Args:
+            message: the SMS message to send.
+        """
+        raise NotImplementedError()
+
 
 class CellularSimulatorError(Exception):
     """ Exceptions thrown when the cellular equipment is unreachable or it
     returns an error after receiving a command. """
-    pass
diff --git a/acts/framework/acts/controllers/chameleon_controller.py b/acts/framework/acts/controllers/chameleon_controller.py
index b9965cf..ed21934 100644
--- a/acts/framework/acts/controllers/chameleon_controller.py
+++ b/acts/framework/acts/controllers/chameleon_controller.py
@@ -15,7 +15,6 @@
 #   limitations under the License.
 
 import logging
-import time
 import xmlrpc.client
 from subprocess import call
 
@@ -105,8 +104,9 @@
         self.port = port
         self.address = "http://{}:{}".format(ip, self.port)
         try:
-            self.client = xmlrpc.client.ServerProxy(
-                self.address, allow_none=True, verbose=False)
+            self.client = xmlrpc.client.ServerProxy(self.address,
+                                                    allow_none=True,
+                                                    verbose=False)
         except ConnectionRefusedError as err:
             self.log.exception(
                 "Failed to connect to Chameleon Device at: {}".format(
diff --git a/acts/framework/acts/controllers/fastboot.py b/acts/framework/acts/controllers/fastboot.py
index 6f47255..db31a19 100755
--- a/acts/framework/acts/controllers/fastboot.py
+++ b/acts/framework/acts/controllers/fastboot.py
@@ -16,7 +16,6 @@
 
 from acts.libs.proc import job
 
-import logging
 from acts import error
 
 
@@ -73,8 +72,10 @@
         if ret == 0 or ignore_status:
             return out
         else:
-            raise FastbootError(
-                cmd=command, stdout=out, stderr=err, ret_code=ret)
+            raise FastbootError(cmd=command,
+                                stdout=out,
+                                stderr=err,
+                                ret_code=ret)
 
     def args(self, *args, **kwargs):
         return job.run(' '.join((self.fastboot_str, ) + args), **kwargs).stdout
diff --git a/acts/framework/acts/controllers/fuchsia_device.py b/acts/framework/acts/controllers/fuchsia_device.py
index a42a722..99d5752 100644
--- a/acts/framework/acts/controllers/fuchsia_device.py
+++ b/acts/framework/acts/controllers/fuchsia_device.py
@@ -14,14 +14,11 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import backoff
+from typing import Optional
 import json
 import logging
 import os
-import random
 import re
-import requests
-import socket
 import subprocess
 import time
 
@@ -31,45 +28,16 @@
 from acts import utils
 from acts.controllers import pdu
 from acts.libs.proc import job
-from acts.utils import get_fuchsia_mdns_ipv6_address
+from acts.utils import get_fuchsia_mdns_ipv6_address, get_interface_ip_addresses
 
-from acts.controllers.fuchsia_lib.audio_lib import FuchsiaAudioLib
-from acts.controllers.fuchsia_lib.backlight_lib import FuchsiaBacklightLib
-from acts.controllers.fuchsia_lib.basemgr_lib import FuchsiaBasemgrLib
-from acts.controllers.fuchsia_lib.bt.avdtp_lib import FuchsiaAvdtpLib
-from acts.controllers.fuchsia_lib.bt.ble_lib import FuchsiaBleLib
-from acts.controllers.fuchsia_lib.bt.bts_lib import FuchsiaBtsLib
-from acts.controllers.fuchsia_lib.bt.gattc_lib import FuchsiaGattcLib
-from acts.controllers.fuchsia_lib.bt.gatts_lib import FuchsiaGattsLib
-from acts.controllers.fuchsia_lib.bt.hfp_lib import FuchsiaHfpLib
-from acts.controllers.fuchsia_lib.bt.rfcomm_lib import FuchsiaRfcommLib
-from acts.controllers.fuchsia_lib.bt.sdp_lib import FuchsiaProfileServerLib
 from acts.controllers.fuchsia_lib.ffx import FFX
-from acts.controllers.fuchsia_lib.gpio_lib import FuchsiaGpioLib
-from acts.controllers.fuchsia_lib.hardware_power_statecontrol_lib import FuchsiaHardwarePowerStatecontrolLib
-from acts.controllers.fuchsia_lib.hwinfo_lib import FuchsiaHwinfoLib
-from acts.controllers.fuchsia_lib.i2c_lib import FuchsiaI2cLib
-from acts.controllers.fuchsia_lib.input_report_lib import FuchsiaInputReportLib
-from acts.controllers.fuchsia_lib.kernel_lib import FuchsiaKernelLib
+from acts.controllers.fuchsia_lib.sl4f import SL4F
 from acts.controllers.fuchsia_lib.lib_controllers.netstack_controller import NetstackController
 from acts.controllers.fuchsia_lib.lib_controllers.wlan_controller import WlanController
 from acts.controllers.fuchsia_lib.lib_controllers.wlan_policy_controller import WlanPolicyController
-from acts.controllers.fuchsia_lib.light_lib import FuchsiaLightLib
-from acts.controllers.fuchsia_lib.location.regulatory_region_lib import FuchsiaRegulatoryRegionLib
-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.sysinfo_lib import FuchsiaSysInfoLib
-from acts.controllers.fuchsia_lib.syslog_lib import FuchsiaSyslogError
-from acts.controllers.fuchsia_lib.syslog_lib import create_syslog_process
-from acts.controllers.fuchsia_lib.utils_lib import SshResults
-from acts.controllers.fuchsia_lib.utils_lib import create_ssh_connection
+from acts.controllers.fuchsia_lib.package_server import PackageServer
+from acts.controllers.fuchsia_lib.ssh import DEFAULT_SSH_PORT, DEFAULT_SSH_USER, SSHConfig, SSHProvider, FuchsiaSSHError
 from acts.controllers.fuchsia_lib.utils_lib import flash
-from acts.controllers.fuchsia_lib.wlan_ap_policy_lib import FuchsiaWlanApPolicyLib
-from acts.controllers.fuchsia_lib.wlan_deprecated_configuration_lib import FuchsiaWlanDeprecatedConfigurationLib
-from acts.controllers.fuchsia_lib.wlan_lib import FuchsiaWlanLib
-from acts.controllers.fuchsia_lib.wlan_policy_lib import FuchsiaWlanPolicyLib
 
 MOBLY_CONTROLLER_CONFIG_NAME = "FuchsiaDevice"
 ACTS_CONTROLLER_REFERENCE_NAME = "fuchsia_devices"
@@ -83,11 +51,7 @@
 FUCHSIA_DEVICE_NO_IP_MSG = "No IP address specified, abort!"
 FUCHSIA_COULD_NOT_GET_DESIRED_STATE = "Could not %s %s."
 FUCHSIA_INVALID_CONTROL_STATE = "Invalid control state (%s). abort!"
-FUCHSIA_SSH_CONFIG_NOT_DEFINED = ("Cannot send ssh commands since the "
-                                  "ssh_config was not specified in the Fuchsia"
-                                  "device config.")
 
-FUCHSIA_SSH_USERNAME = "fuchsia"
 FUCHSIA_TIME_IN_NANOSECONDS = 1000000000
 
 SL4F_APK_NAME = "com.googlecode.android_scripting"
@@ -96,25 +60,15 @@
 DAEMON_ACTIVATED_STATES = ["running", "start"]
 DAEMON_DEACTIVATED_STATES = ["stop", "stopped"]
 
-FUCHSIA_DEFAULT_LOG_CMD = 'iquery --absolute_paths --cat --format= --recursive'
-FUCHSIA_DEFAULT_LOG_ITEMS = [
-    '/hub/c/scenic.cmx/[0-9]*/out/objects',
-    '/hub/c/root_presenter.cmx/[0-9]*/out/objects',
-    '/hub/c/wlanstack2.cmx/[0-9]*/out/public',
-    '/hub/c/basemgr.cmx/[0-9]*/out/objects'
-]
-
 FUCHSIA_RECONNECT_AFTER_REBOOT_TIME = 5
 
 CHANNEL_OPEN_TIMEOUT = 5
 
-FUCHSIA_GET_VERSION_CMD = 'cat /config/build-info/version'
-
 FUCHSIA_REBOOT_TYPE_SOFT = 'soft'
 FUCHSIA_REBOOT_TYPE_SOFT_AND_FLASH = 'flash'
 FUCHSIA_REBOOT_TYPE_HARD = 'hard'
 
-FUCHSIA_DEFAULT_CONNECT_TIMEOUT = 60
+FUCHSIA_DEFAULT_CONNECT_TIMEOUT = 90
 FUCHSIA_DEFAULT_COMMAND_TIMEOUT = 60
 
 FUCHSIA_DEFAULT_CLEAN_UP_COMMAND_TIMEOUT = 15
@@ -125,12 +79,17 @@
 MDNS_LOOKUP_RETRY_MAX = 3
 
 VALID_ASSOCIATION_MECHANISMS = {None, 'policy', 'drivers'}
+IP_ADDRESS_TIMEOUT = 15
 
 
 class FuchsiaDeviceError(signals.ControllerError):
     pass
 
 
+class FuchsiaConfigError(signals.ControllerError):
+    """Incorrect FuchsiaDevice configuration."""
+
+
 def create(configs):
     if not configs:
         raise FuchsiaDeviceError(FUCHSIA_DEVICE_EMPTY_CONFIG_MSG)
@@ -212,23 +171,34 @@
         self.conf_data = fd_conf_data
         if "ip" not in fd_conf_data:
             raise FuchsiaDeviceError(FUCHSIA_DEVICE_NO_IP_MSG)
-        self.ip = fd_conf_data["ip"]
-        self.orig_ip = fd_conf_data["ip"]
-        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)
-        self.product_type = fd_conf_data.get("product_type", None)
-        self.board_type = fd_conf_data.get("board_type", None)
-        self.build_number = fd_conf_data.get("build_number", None)
-        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)
+        self.ip: str = fd_conf_data["ip"]
+        self.orig_ip: str = fd_conf_data["ip"]
+        self.sl4f_port: int = fd_conf_data.get("sl4f_port", 80)
+        self.ssh_port: int = fd_conf_data.get("ssh_port", DEFAULT_SSH_PORT)
+        self.ssh_config: Optional[str] = fd_conf_data.get("ssh_config", None)
+        self.ssh_priv_key: Optional[str] = fd_conf_data.get(
+            "ssh_priv_key", None)
+        self.authorized_file: Optional[str] = fd_conf_data.get(
+            "authorized_file_loc", None)
+        self.serial_number: Optional[str] = fd_conf_data.get(
+            "serial_number", None)
+        self.device_type: Optional[str] = fd_conf_data.get("device_type", None)
+        self.product_type: Optional[str] = fd_conf_data.get(
+            "product_type", None)
+        self.board_type: Optional[str] = fd_conf_data.get("board_type", None)
+        self.build_number: Optional[str] = fd_conf_data.get(
+            "build_number", None)
+        self.build_type: Optional[str] = fd_conf_data.get("build_type", None)
+        self.server_path: Optional[str] = fd_conf_data.get("server_path", None)
+        self.specific_image: Optional[str] = fd_conf_data.get(
+            "specific_image", None)
+        self.ffx_binary_path: Optional[str] = fd_conf_data.get(
+            "ffx_binary_path", None)
+        # Path to a tar.gz archive with pm and amber-files, as necessary for
+        # starting a package server.
+        self.packages_archive_path: Optional[str] = fd_conf_data.get(
+            "packages_archive_path", None)
+        self.mdns_name: Optional[str] = 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.
@@ -238,8 +208,7 @@
         self._set_control_path_config(self.ssh_config, generated_ssh_config)
         self.ssh_config = generated_ssh_config
 
-        self.ssh_username = fd_conf_data.get("ssh_username",
-                                             FUCHSIA_SSH_USERNAME)
+        self.ssh_username = fd_conf_data.get("ssh_username", DEFAULT_SSH_USER)
         self.hard_reboot_on_fail = fd_conf_data.get("hard_reboot_on_fail",
                                                     False)
         self.take_bug_report_on_fail = fd_conf_data.get(
@@ -247,7 +216,6 @@
         self.device_pdu_config = fd_conf_data.get("PduDevice", None)
         self.config_country_code = fd_conf_data.get(
             'country_code', FUCHSIA_DEFAULT_COUNTRY_CODE_US).upper()
-        self._persistent_ssh_conn = None
 
         # WLAN interface info is populated inside configure_wlan
         self.wlan_client_interfaces = {}
@@ -269,11 +237,8 @@
         self.default_preserve_saved_networks = fd_conf_data.get(
             'preserve_saved_networks', True)
 
-        if utils.is_valid_ipv4_address(self.ip):
-            self.address = "http://{}:{}".format(self.ip, self.sl4f_port)
-        elif utils.is_valid_ipv6_address(self.ip):
-            self.address = "http://[{}]:{}".format(self.ip, self.sl4f_port)
-        else:
+        if not utils.is_valid_ipv4_address(
+                self.ip) and not utils.is_valid_ipv6_address(self.ip):
             mdns_ip = None
             for retry_counter in range(MDNS_LOOKUP_RETRY_MAX):
                 mdns_ip = get_fuchsia_mdns_ipv6_address(self.ip)
@@ -286,22 +251,14 @@
                 # 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:
                 raise ValueError('Invalid IP: %s' % self.ip)
 
         self.log = acts_logger.create_tagged_trace_logger(
             "FuchsiaDevice | %s" % self.orig_ip)
 
-        self.init_address = self.address + "/init"
-        self.cleanup_address = self.address + "/cleanup"
-        self.print_address = self.address + "/print_clients"
         self.ping_rtt_match = re.compile(r'RTT Min/Max/Avg '
                                          r'= \[ (.*?) / (.*?) / (.*?) \] ms')
-
-        # TODO(): Come up with better client numbering system
-        self.client_id = "FuchsiaClient" + str(random.randint(0, 1000000))
-        self.test_counter = 0
         self.serial = re.sub('[.:%]', '_', self.ip)
         log_path_base = getattr(logging, 'log_path', '/tmp/logs')
         self.log_path = os.path.join(log_path_base,
@@ -309,19 +266,79 @@
         self.fuchsia_log_file_path = os.path.join(
             self.log_path, "fuchsialog_%s_debug.txt" % self.serial)
         self.log_process = None
+        self.package_server = None
 
-        self.init_libraries()
+        self.init_controllers()
 
-        self.setup_commands = fd_conf_data.get('setup_commands', [])
-        self.teardown_commands = fd_conf_data.get('teardown_commands', [])
+    @property
+    def sl4f(self):
+        """Get the sl4f module configured for this device.
 
-        try:
-            self.start_services()
-            self.run_commands_from_config(self.setup_commands)
-        except Exception as e:
-            # Prevent a threading error, since controller isn't fully up yet.
-            self.clean_up()
-            raise e
+        The sl4f module uses lazy-initialization; it will initialize an sl4f
+        server on the host device when it is required.
+        """
+        if not hasattr(self, '_sl4f'):
+            self._sl4f = SL4F(self.ssh, self.sl4f_port)
+            self.log.info('Started SL4F server')
+        return self._sl4f
+
+    @sl4f.deleter
+    def sl4f(self):
+        if not hasattr(self, '_sl4f'):
+            return
+        del self._sl4f
+
+    @property
+    def ssh(self):
+        """Get the SSH provider module configured for this device."""
+        if not hasattr(self, '_ssh'):
+            if not self.ssh_port:
+                raise FuchsiaConfigError(
+                    'Must provide "ssh_port: <int>" in the device config')
+            if not self.ssh_priv_key:
+                raise FuchsiaConfigError(
+                    'Must provide "ssh_priv_key: <file path>" in the device config'
+                )
+            self._ssh = SSHProvider(
+                SSHConfig(self.ip, self.ssh_priv_key, port=self.ssh_port))
+        return self._ssh
+
+    @ssh.deleter
+    def ssh(self):
+        if not hasattr(self, '_ssh'):
+            return
+        del self._ssh
+
+    @property
+    def ffx(self):
+        """Get the ffx module configured for this device.
+
+        The ffx module uses lazy-initialization; it will initialize an ffx
+        connection to the device when it is required.
+
+        If ffx needs to be reinitialized, delete the "ffx" property and attempt
+        access again. Note re-initialization will interrupt any running ffx
+        calls.
+        """
+        if not hasattr(self, '_ffx'):
+            if not self.ffx_binary_path:
+                raise FuchsiaConfigError(
+                    'Must provide "ffx_binary_path: <path to FFX binary>" in the device config'
+                )
+            if not self.mdns_name:
+                raise FuchsiaConfigError(
+                    'Must provide "mdns_name: <device mDNS name>" in the device config'
+                )
+            self._ffx = FFX(self.ffx_binary_path, self.mdns_name, self.ip,
+                            self.ssh_priv_key)
+        return self._ffx
+
+    @ffx.deleter
+    def ffx(self):
+        if not hasattr(self, '_ffx'):
+            return
+        self._ffx.clean_up()
+        del self._ffx
 
     def _set_control_path_config(self, old_config, new_config):
         """Given an input ssh_config, write to a new config with proper
@@ -344,121 +361,7 @@
         with open(new_config, 'w') as file:
             file.write(ssh_config_copy)
 
-    def init_libraries(self):
-        # Grab commands from FuchsiaAudioLib
-        self.audio_lib = FuchsiaAudioLib(self.address, self.test_counter,
-                                         self.client_id)
-
-        # Grab commands from FuchsiaAvdtpLib
-        self.avdtp_lib = FuchsiaAvdtpLib(self.address, self.test_counter,
-                                         self.client_id)
-
-        # Grab commands from FuchsiaHfpLib
-        self.hfp_lib = FuchsiaHfpLib(self.address, self.test_counter,
-                                     self.client_id)
-
-        # Grab commands from FuchsiaRfcommLib
-        self.rfcomm_lib = FuchsiaRfcommLib(self.address, self.test_counter,
-                                           self.client_id)
-
-        # Grab commands from FuchsiaLightLib
-        self.light_lib = FuchsiaLightLib(self.address, self.test_counter,
-                                         self.client_id)
-
-        # Grab commands from FuchsiaBacklightLib
-        self.backlight_lib = FuchsiaBacklightLib(self.address,
-                                                 self.test_counter,
-                                                 self.client_id)
-
-        # Grab commands from FuchsiaBasemgrLib
-        self.basemgr_lib = FuchsiaBasemgrLib(self.address, self.test_counter,
-                                             self.client_id)
-        # Grab commands from FuchsiaBleLib
-        self.ble_lib = FuchsiaBleLib(self.address, self.test_counter,
-                                     self.client_id)
-        # Grab commands from FuchsiaBtsLib
-        self.bts_lib = FuchsiaBtsLib(self.address, self.test_counter,
-                                     self.client_id)
-        # Grab commands from FuchsiaGattcLib
-        self.gattc_lib = FuchsiaGattcLib(self.address, self.test_counter,
-                                         self.client_id)
-        # Grab commands from FuchsiaGattsLib
-        self.gatts_lib = FuchsiaGattsLib(self.address, self.test_counter,
-                                         self.client_id)
-
-        # Grab commands from FuchsiaGpioLib
-        self.gpio_lib = FuchsiaGpioLib(self.address, self.test_counter,
-                                       self.client_id)
-
-        # Grab commands from FuchsiaHardwarePowerStatecontrolLib
-        self.hardware_power_statecontrol_lib = (
-            FuchsiaHardwarePowerStatecontrolLib(self.address,
-                                                self.test_counter,
-                                                self.client_id))
-
-        # Grab commands from FuchsiaHwinfoLib
-        self.hwinfo_lib = FuchsiaHwinfoLib(self.address, self.test_counter,
-                                           self.client_id)
-
-        # Grab commands from FuchsiaI2cLib
-        self.i2c_lib = FuchsiaI2cLib(self.address, self.test_counter,
-                                     self.client_id)
-
-        # Grab commands from FuchsiaInputReportLib
-        self.input_report_lib = FuchsiaInputReportLib(self.address,
-                                                      self.test_counter,
-                                                      self.client_id)
-
-        # Grab commands from FuchsiaKernelLib
-        self.kernel_lib = FuchsiaKernelLib(self.address, self.test_counter,
-                                           self.client_id)
-
-        # Grab commands from FuchsiaLoggingLib
-        self.logging_lib = FuchsiaLoggingLib(self.address, self.test_counter,
-                                             self.client_id)
-
-        # Grab commands from FuchsiaNetstackLib
-        self.netstack_lib = FuchsiaNetstackLib(self.address, self.test_counter,
-                                               self.client_id)
-
-        # Grab commands from FuchsiaLightLib
-        self.ram_lib = FuchsiaRamLib(self.address, self.test_counter,
-                                     self.client_id)
-
-        # Grab commands from FuchsiaProfileServerLib
-        self.sdp_lib = FuchsiaProfileServerLib(self.address, self.test_counter,
-                                               self.client_id)
-
-        # Grab commands from FuchsiaRegulatoryRegionLib
-        self.regulatory_region_lib = FuchsiaRegulatoryRegionLib(
-            self.address, self.test_counter, self.client_id)
-
-        # Grab commands from FuchsiaSysInfoLib
-        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,
-                                                  self.test_counter,
-                                                  self.client_id))
-
-        # Grab commands from FuchsiaWlanLib
-        self.wlan_lib = FuchsiaWlanLib(self.address, self.test_counter,
-                                       self.client_id)
-
-        # Grab commands from FuchsiaWlanApPolicyLib
-        self.wlan_ap_policy_lib = FuchsiaWlanApPolicyLib(
-            self.address, self.test_counter, self.client_id)
-
-        # Grab commands from FuchsiaWlanPolicyLib
-        self.wlan_policy_lib = FuchsiaWlanPolicyLib(self.address,
-                                                    self.test_counter,
-                                                    self.client_id)
-
+    def init_controllers(self):
         # Contains Netstack functions
         self.netstack_controller = NetstackController(self)
 
@@ -466,49 +369,27 @@
         self.wlan_controller = WlanController(self)
 
         # Contains WLAN policy functions like save_network, remove_network, etc
-        self.wlan_policy_controller = WlanPolicyController(self)
+        self.wlan_policy_controller = WlanPolicyController(self.sl4f, self.ffx)
 
-    @backoff.on_exception(
-        backoff.constant,
-        (ConnectionRefusedError, requests.exceptions.ConnectionError),
-        interval=1.5,
-        max_tries=4)
-    def init_sl4f_connection(self):
-        """Initializes HTTP connection with SL4F server."""
-        self.log.debug("Initializing SL4F server connection")
-        init_data = json.dumps({
-            "jsonrpc": "2.0",
-            "id": self.build_id(self.test_counter),
-            "method": "sl4f.sl4f_init",
-            "params": {
-                "client_id": self.client_id
-            }
-        })
-
-        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.
-
-        If ffx has already been initialized, it will be reinitialized. This will
-        break any running tests calling ffx for this device.
-        """
-        self.log.debug("Initializing ffx connection")
-
-        if not self.ffx_binary_path:
-            raise ValueError(
-                'Must provide "ffx_binary_path: <path to FFX binary>" in the device config'
+    def start_package_server(self):
+        if not self.packages_archive_path:
+            self.log.warn(
+                "packages_archive_path is not specified. "
+                "Assuming a package server is already running and configured on "
+                "the DUT. If this is not the case, either run your own package "
+                "server, or configure these fields appropriately. "
+                "This is usually required for the Fuchsia iPerf3 client or "
+                "other testing utilities not on device cache.")
+            return
+        if self.package_server:
+            self.log.warn(
+                "Skipping to start the package server since is already running"
             )
-        if not self.mdns_name:
-            raise ValueError(
-                'Must provide "mdns_name: <device mDNS name>" in the device config'
-            )
+            return
 
-        if hasattr(self, 'ffx'):
-            self.ffx.clean_up()
-
-        self.ffx = FFX(self.ffx_binary_path, self.mdns_name, self.ssh_priv_key)
+        self.package_server = PackageServer(self.packages_archive_path)
+        self.package_server.start()
+        self.package_server.configure_device(self.ssh)
 
     def run_commands_from_config(self, cmd_dicts):
         """Runs commands on the Fuchsia device from the config file. Useful for
@@ -519,6 +400,10 @@
                 'cmd': string, command to run on device
                 'timeout': int, seconds to wait for command to run (optional)
                 'skip_status_code_check': bool, disregard errors if true
+
+        Raises:
+            FuchsiaDeviceError: if any of the commands return a non-zero status
+                code and skip_status_code_check is false or undefined.
         """
         for cmd_dict in cmd_dicts:
             try:
@@ -533,29 +418,19 @@
             skip_status_code_check = 'true' == str(
                 cmd_dict.get('skip_status_code_check', False)).lower()
 
-            self.log.info(
-                'Running command "%s".%s' %
-                (cmd, ' Ignoring result.' if skip_status_code_check else ''))
-            result = self.send_command_ssh(
-                cmd,
-                timeout=timeout,
-                skip_status_code_check=skip_status_code_check)
+            if skip_status_code_check:
+                self.log.info(f'Running command "{cmd}" and ignoring result.')
+            else:
+                self.log.info(f'Running command "{cmd}".')
 
-            if isinstance(result, Exception):
-                raise result
-
-            elif not skip_status_code_check and result.stderr:
-                raise FuchsiaDeviceError(
-                    'Error when running command "%s": %s' %
-                    (cmd, result.stderr))
-
-    def build_id(self, test_id):
-        """Concatenates client_id and test_id to form a command_id
-
-        Args:
-            test_id: string, unique identifier of test command
-        """
-        return self.client_id + "." + str(test_id)
+            try:
+                result = self.ssh.run(cmd, timeout_sec=timeout)
+                self.log.debug(result)
+            except FuchsiaSSHError as e:
+                if not skip_status_code_check:
+                    raise FuchsiaDeviceError(
+                        'Failed device specific commands for initial configuration'
+                    ) from e
 
     def configure_wlan(self,
                        association_mechanism=None,
@@ -593,6 +468,7 @@
 
         # Allows for wlan to be set up differently in different tests
         if self.association_mechanism:
+            self.log.info('Deconfiguring WLAN')
             self.deconfigure_wlan()
 
         self.association_mechanism = association_mechanism
@@ -612,8 +488,7 @@
         else:
             # This requires SL4F calls, so it can only happen with actual
             # devices, not with unit tests.
-            self.wlan_policy_controller._configure_wlan(
-                preserve_saved_networks)
+            self.wlan_policy_controller.configure_wlan(preserve_saved_networks)
 
         # Retrieve WLAN client and AP interfaces
         self.wlan_controller.update_wlan_interfaces()
@@ -639,139 +514,84 @@
         self.association_mechanism = None
 
     def reboot(self,
-               use_ssh=False,
-               unreachable_timeout=FUCHSIA_DEFAULT_CONNECT_TIMEOUT,
-               ping_timeout=FUCHSIA_DEFAULT_CONNECT_TIMEOUT,
-               ssh_timeout=FUCHSIA_DEFAULT_CONNECT_TIMEOUT,
-               reboot_type=FUCHSIA_REBOOT_TYPE_SOFT,
-               testbed_pdus=None):
+               use_ssh: bool = False,
+               unreachable_timeout: int = FUCHSIA_DEFAULT_CONNECT_TIMEOUT,
+               ping_timeout: int = FUCHSIA_DEFAULT_CONNECT_TIMEOUT,
+               ssh_timeout: int = FUCHSIA_DEFAULT_CONNECT_TIMEOUT,
+               reboot_type: int = FUCHSIA_REBOOT_TYPE_SOFT,
+               testbed_pdus: list[pdu.PduDevice] = None) -> None:
         """Reboot a FuchsiaDevice.
 
         Soft reboots the device, verifies it becomes unreachable, then verifies
         it comes back online. Re-initializes services so the tests can continue.
 
         Args:
-            use_ssh: bool, if True, use fuchsia shell command via ssh to reboot
+            use_ssh: if True, use fuchsia shell command via ssh to reboot
                 instead of SL4F.
-            unreachable_timeout: int, time to wait for device to become
-                unreachable.
-            ping_timeout: int, time to wait for device to respond to pings.
-            ssh_timeout: int, time to wait for device to be reachable via ssh.
-            reboot_type: boolFUCHSIA_REBOOT_TYPE_SOFT or
-                FUCHSIA_REBOOT_TYPE_HARD
-            testbed_pdus: list, all testbed PDUs
+            unreachable_timeout: time to wait for device to become unreachable.
+            ping_timeout:time to wait for device to respond to pings.
+            ssh_timeout: time to wait for device to be reachable via ssh.
+            reboot_type: 'soft', 'hard' or 'flash'.
+            testbed_pdus: all testbed PDUs.
 
         Raises:
-            ConnectionError, if device fails to become unreachable, fails to
-                come back up, or if SL4F does not setup correctly.
+            ConnectionError, if device fails to become unreachable or fails to
+                come back up.
         """
-        skip_unreachable_check = False
-        # Call Reboot
         if reboot_type == FUCHSIA_REBOOT_TYPE_SOFT:
             if use_ssh:
-                self.log.info('Sending reboot command via SSH.')
-                with utils.SuppressLogOutput():
-                    self.clean_up_services()
-                    self.send_command_ssh(
+                self.log.info('Soft rebooting via SSH')
+                try:
+                    self.ssh.run(
                         'dm reboot',
-                        timeout=FUCHSIA_RECONNECT_AFTER_REBOOT_TIME,
-                        skip_status_code_check=True)
+                        timeout_sec=FUCHSIA_RECONNECT_AFTER_REBOOT_TIME)
+                except FuchsiaSSHError as e:
+                    if 'closed by remote host' not in e.result.stderr:
+                        raise e
             else:
-                self.log.info('Calling SL4F reboot command.')
-                with utils.SuppressLogOutput():
-                    self.hardware_power_statecontrol_lib.suspendReboot(
-                        timeout=3)
-                    self.clean_up_services()
-        elif reboot_type == FUCHSIA_REBOOT_TYPE_SOFT_AND_FLASH:
-            flash(self, use_ssh, FUCHSIA_RECONNECT_AFTER_REBOOT_TIME)
-            skip_unreachable_check = True
+                self.log.info('Soft rebooting via SL4F')
+                self.sl4f.hardware_power_statecontrol_lib.suspendReboot(
+                    timeout=3)
+            self._check_unreachable(timeout_sec=unreachable_timeout)
+
         elif reboot_type == FUCHSIA_REBOOT_TYPE_HARD:
-            self.log.info('Power cycling FuchsiaDevice (%s)' % self.ip)
+            self.log.info('Hard rebooting via PDU')
             if not testbed_pdus:
                 raise AttributeError('Testbed PDUs must be supplied '
                                      'to hard reboot a fuchsia_device.')
             device_pdu, device_pdu_port = pdu.get_pdu_port_for_device(
                 self.device_pdu_config, testbed_pdus)
-            with utils.SuppressLogOutput():
-                self.clean_up_services()
-            self.log.info('Killing power to FuchsiaDevice (%s)...' % self.ip)
+            self.log.info('Killing power to FuchsiaDevice')
             device_pdu.off(str(device_pdu_port))
-        else:
-            raise ValueError('Invalid reboot type: %s' % reboot_type)
-        if not skip_unreachable_check:
-            # Wait for unreachable
-            self.log.info('Verifying device is unreachable.')
-            timeout = time.time() + unreachable_timeout
-            while (time.time() < timeout):
-                if utils.can_ping(job, self.ip):
-                    self.log.debug('Device is still pingable. Retrying.')
-                else:
-                    if reboot_type == FUCHSIA_REBOOT_TYPE_HARD:
-                        self.log.info(
-                            'Restoring power to FuchsiaDevice (%s)...' %
-                            self.ip)
-                        device_pdu.on(str(device_pdu_port))
-                    break
-            else:
-                self.log.info(
-                    'Device failed to go offline. Restarting services...')
-                self.start_services()
-                raise ConnectionError('Device never went down.')
-            self.log.info('Device is unreachable as expected.')
-        if reboot_type == FUCHSIA_REBOOT_TYPE_HARD:
-            self.log.info('Restoring power to FuchsiaDevice (%s)...' % self.ip)
+            self._check_unreachable(timeout_sec=unreachable_timeout)
+            self.log.info('Restoring power to FuchsiaDevice')
             device_pdu.on(str(device_pdu_port))
 
-        self.log.info('Waiting for device to respond to pings.')
-        end_time = time.time() + ping_timeout
-        while time.time() < end_time:
-            if utils.can_ping(job, self.ip):
-                break
-            else:
-                self.log.debug('Device is not pingable. Retrying in 1 second.')
-                time.sleep(1)
+        elif reboot_type == FUCHSIA_REBOOT_TYPE_SOFT_AND_FLASH:
+            flash(self, use_ssh, FUCHSIA_RECONNECT_AFTER_REBOOT_TIME)
+
         else:
-            raise ConnectionError('Device never came back online.')
-        self.log.info('Device responded to pings.')
+            raise ValueError('Invalid reboot type: %s' % reboot_type)
+
+        self._check_reachable(timeout_sec=ping_timeout)
+
+        # Cleanup services
+        self.stop_services()
 
         self.log.info('Waiting for device to allow ssh connection.')
         end_time = time.time() + ssh_timeout
         while time.time() < end_time:
             try:
-                self.send_command_ssh('\n')
-            except Exception:
-                self.log.debug(
-                    'Could not SSH to device. Retrying in 1 second.')
-                time.sleep(1)
+                self.ssh.run('echo')
+            except Exception as e:
+                self.log.debug(f'Retrying SSH to device. Details: {e}')
             else:
                 break
         else:
             raise ConnectionError('Failed to connect to device via SSH.')
         self.log.info('Device now available via ssh.')
 
-        # Creating new log process, start it, start new persistent ssh session,
-        # start SL4F, and connect via SL4F
-        self.log.info(f'Restarting services on FuchsiaDevice {self.ip}')
-        self.start_services()
-
-        # Verify SL4F is up.
-        self.log.info('Verifying SL4F commands can run.')
-        try:
-            self.hwinfo_lib.getDeviceInfo()
-        except Exception as err:
-            raise ConnectionError(
-                'Failed to connect and run command via SL4F. Err: %s' % err)
-
-        # Reconfigure country code, as it does not persist after reboots
-        self.configure_regulatory_domain(self.config_country_code)
-        try:
-            self.run_commands_from_config(self.setup_commands)
-        except FuchsiaDeviceError:
-            # Prevent a threading error, since controller isn't fully up yet.
-            self.clean_up()
-            raise FuchsiaDeviceError(
-                'Failed to run setup commands after reboot.')
-
+        # TODO (b/246852449): Move configure_wlan to other controllers.
         # If wlan was configured before reboot, it must be configured again
         # after rebooting, as it was before reboot. No preserving should occur.
         if self.association_mechanism:
@@ -782,68 +602,39 @@
                 association_mechanism=pre_reboot_association_mechanism,
                 preserve_saved_networks=False)
 
-        self.log.info(
-            'Device has rebooted, SL4F is reconnected and functional.')
+        self.log.info('Device has rebooted')
 
-    def send_command_ssh(self,
-                         test_cmd,
-                         connect_timeout=FUCHSIA_DEFAULT_CONNECT_TIMEOUT,
-                         timeout=FUCHSIA_DEFAULT_COMMAND_TIMEOUT,
-                         skip_status_code_check=False):
-        """Sends an SSH command to a Fuchsia device
-
-        Args:
-            test_cmd: string, command to send to Fuchsia device over SSH.
-            connect_timeout: Timeout to wait for connecting via SSH.
-            timeout: Timeout to wait for a command to complete.
-            skip_status_code_check: Whether to check for the status code.
-
-        Returns:
-            A SshResults object containing the results of the ssh command.
-        """
-        command_result = False
-        ssh_conn = None
-        if not self.ssh_config:
-            self.log.warning(FUCHSIA_SSH_CONFIG_NOT_DEFINED)
-        else:
-            try:
-                ssh_conn = create_ssh_connection(
-                    self.ip,
-                    self.ssh_username,
-                    self.ssh_config,
-                    ssh_port=self.ssh_port,
-                    connect_timeout=connect_timeout)
-                cmd_result_stdin, cmd_result_stdout, cmd_result_stderr = (
-                    ssh_conn.exec_command(test_cmd, timeout=timeout))
-                if not skip_status_code_check:
-                    command_result = SshResults(cmd_result_stdin,
-                                                cmd_result_stdout,
-                                                cmd_result_stderr,
-                                                cmd_result_stdout.channel)
-            except Exception as e:
-                self.log.warning("Problem running ssh command: %s"
-                                 "\n Exception: %s" % (test_cmd, e))
-                return e
-            finally:
-                if ssh_conn is not None:
-                    ssh_conn.close()
-        return command_result
-
-    def version(self, timeout=FUCHSIA_DEFAULT_COMMAND_TIMEOUT):
+    def version(self):
         """Returns the version of Fuchsia running on the device.
 
-        Args:
-            timeout: (int) Seconds to wait for command to run.
-
         Returns:
-            A string containing the Fuchsia version number.
-            For example, "5.20210713.2.1".
+            A string containing the Fuchsia version number or nothing if there
+            is no version information attached during the build.
+            For example, "5.20210713.2.1" or "".
 
         Raises:
-            DeviceOffline: If SSH to the device fails.
+            FFXTimeout: when the command times out.
+            FFXError: when the command returns non-zero and skip_status_code_check is False.
         """
-        return self.send_command_ssh(FUCHSIA_GET_VERSION_CMD,
-                                     timeout=timeout).stdout
+        target_info_json = self.ffx.run("target show --json").stdout
+        target_info = json.loads(target_info_json)
+        build_info = [
+            entry for entry in target_info if entry["label"] == "build"
+        ]
+        if len(build_info) != 1:
+            self.log.warning(
+                f'Expected one entry with label "build", found {build_info}')
+            return ""
+        version_info = [
+            child for child in build_info[0]["child"]
+            if child["label"] == "version"
+        ]
+        if len(version_info) != 1:
+            self.log.warning(
+                f'Expected one entry child with label "version", found {build_info}'
+            )
+            return ""
+        return version_info[0]["value"]
 
     def ping(self,
              dest_ip,
@@ -880,11 +671,13 @@
         self.log.debug("Pinging %s..." % dest_ip)
         if not additional_ping_params:
             additional_ping_params = ''
-        ping_result = self.send_command_ssh(
-            'ping -c %s -i %s -t %s -s %s %s %s' %
-            (count, interval, timeout, size, additional_ping_params, dest_ip))
-        if isinstance(ping_result, job.Error):
-            ping_result = ping_result.result
+
+        try:
+            ping_result = self.ssh.run(
+                f'ping -c {count} -i {interval} -t {timeout} -s {size} '
+                f'{additional_ping_params} {dest_ip}')
+        except FuchsiaSSHError as e:
+            ping_result = e.result
 
         if ping_result.stderr:
             status = False
@@ -921,27 +714,9 @@
                                 additional_ping_params=additional_ping_params)
         return ping_result['status']
 
-    def print_clients(self):
-        """Gets connected clients from SL4F server"""
-        self.log.debug("Request to print clients")
-        print_id = self.build_id(self.test_counter)
-        print_args = {}
-        print_method = "sl4f.sl4f_print_clients"
-        data = json.dumps({
-            "jsonrpc": "2.0",
-            "id": print_id,
-            "method": print_method,
-            "params": print_args
-        })
-
-        r = requests.get(url=self.print_address, data=data).json()
-        self.test_counter += 1
-
-        return r
-
     def clean_up(self):
         """Cleans up the FuchsiaDevice object, releases any resources it
-        claimed, and restores saved networks is applicable. For reboots, use
+        claimed, and restores saved networks if applicable. For reboots, use
         clean_up_services only.
 
         Note: Any exceptions thrown in this method must be caught and handled,
@@ -951,151 +726,113 @@
         # If and only if wlan is configured, and using the policy layer
         if self.association_mechanism == 'policy':
             try:
-                self.wlan_policy_controller._clean_up()
+                self.wlan_policy_controller.clean_up()
             except Exception as err:
                 self.log.warning('Unable to clean up WLAN Policy layer: %s' %
                                  err)
-        try:
-            self.run_commands_from_config(self.teardown_commands)
-        except Exception as err:
-            self.log.warning('Failed to run teardown_commands: %s' % err)
 
-        # This MUST be run, otherwise syslog threads will never join.
-        self.clean_up_services()
+        self.stop_services()
 
-    def clean_up_services(self):
-        """ Cleans up FuchsiaDevice services (e.g. SL4F). Subset of clean_up,
-        to be used for reboots, when testing is to continue (as opposed to
-        teardown after testing is finished.)
-        """
-        cleanup_id = self.build_id(self.test_counter)
-        cleanup_args = {}
-        cleanup_method = "sl4f.sl4f_cleanup"
-        data = json.dumps({
-            "jsonrpc": "2.0",
-            "id": cleanup_id,
-            "method": cleanup_method,
-            "params": cleanup_args
-        })
+        if self.package_server:
+            self.package_server.clean_up()
 
-        try:
-            response = requests.get(
-                url=self.cleanup_address,
-                data=data,
-                timeout=FUCHSIA_DEFAULT_CLEAN_UP_COMMAND_TIMEOUT).json()
-            self.log.debug(response)
-        except Exception as err:
-            self.log.exception("Cleanup request failed with %s:" % err)
-        finally:
-            self.test_counter += 1
-            self.stop_services()
+    def get_interface_ip_addresses(self, interface):
+        return get_interface_ip_addresses(self, interface)
 
-    def check_process_state(self, process_name):
-        """Checks the state of a process on the Fuchsia device
-
-        Returns:
-            True if the process_name is running
-            False if process_name is not running
-        """
-        ps_cmd = self.send_command_ssh("ps")
-        return process_name in ps_cmd.stdout
-
-    def check_process_with_expectation(self, process_name, expectation=None):
-        """Checks the state of a process on the Fuchsia device and returns
-        true or false depending the stated expectation
+    def wait_for_ipv4_addr(self, interface: str) -> None:
+        """Checks if device has an ipv4 private address. Sleeps 1 second between
+        retries.
 
         Args:
-            process_name: The name of the process to check for.
-            expectation: The state expectation of state of process
-        Returns:
-            True if the state of the process matches the expectation
-            False if the state of the process does not match the expectation
-        """
-        process_state = self.check_process_state(process_name)
-        if expectation in DAEMON_ACTIVATED_STATES:
-            return process_state
-        elif expectation in DAEMON_DEACTIVATED_STATES:
-            return not process_state
-        else:
-            raise ValueError("Invalid expectation value (%s). abort!" %
-                             expectation)
+            interface: name of interface from which to get ipv4 address.
 
-    def control_daemon(self, process_name, action):
-        """Starts or stops a process on a Fuchsia device
-
-        Args:
-            process_name: the name of the process to start or stop
-            action: specify whether to start or stop a process
+        Raises:
+            ConnectionError, if device does not have an ipv4 address after all
+            timeout.
         """
-        if not (process_name[-4:] == '.cmx' or process_name[-4:] == '.cml'):
-            process_name = '%s.cmx' % process_name
-        unable_to_connect_msg = None
-        process_state = False
-        try:
-            if not self._persistent_ssh_conn:
-                self._persistent_ssh_conn = (create_ssh_connection(
-                    self.ip,
-                    self.ssh_username,
-                    self.ssh_config,
-                    ssh_port=self.ssh_port))
-            self._persistent_ssh_conn.exec_command(
-                "killall %s" % process_name, timeout=CHANNEL_OPEN_TIMEOUT)
-            # This command will effectively stop the process but should
-            # be used as a cleanup before starting a process.  It is a bit
-            # confusing to have the msg saying "attempting to stop
-            # the process" after the command already tried but since both start
-            # and stop need to run this command, this is the best place
-            # for the command.
-            if action in DAEMON_ACTIVATED_STATES:
-                self.log.debug("Attempting to start Fuchsia "
-                               "devices services.")
-                self._persistent_ssh_conn.exec_command(
-                    "run fuchsia-pkg://fuchsia.com/%s#meta/%s &" %
-                    (process_name[:-4], process_name))
-                process_initial_msg = (
-                    "%s has not started yet. Waiting %i second and "
-                    "checking again." %
-                    (process_name, DAEMON_INIT_TIMEOUT_SEC))
-                process_timeout_msg = ("Timed out waiting for %s to start." %
-                                       process_name)
-                unable_to_connect_msg = ("Unable to start %s no Fuchsia "
-                                         "device via SSH. %s may not "
-                                         "be started." %
-                                         (process_name, process_name))
-            elif action in DAEMON_DEACTIVATED_STATES:
-                process_initial_msg = ("%s is running. Waiting %i second and "
-                                       "checking again." %
-                                       (process_name, DAEMON_INIT_TIMEOUT_SEC))
-                process_timeout_msg = ("Timed out waiting trying to kill %s." %
-                                       process_name)
-                unable_to_connect_msg = ("Unable to stop %s on Fuchsia "
-                                         "device via SSH. %s may "
-                                         "still be running." %
-                                         (process_name, process_name))
+        self.log.info(
+            f'Checking for valid ipv4 addr. Retry {IP_ADDRESS_TIMEOUT} seconds.'
+        )
+        timeout = time.time() + IP_ADDRESS_TIMEOUT
+        while time.time() < timeout:
+            ip_addrs = self.get_interface_ip_addresses(interface)
+
+            if len(ip_addrs['ipv4_private']) > 0:
+                self.log.info("Device has an ipv4 address: "
+                              f"{ip_addrs['ipv4_private'][0]}")
+                break
             else:
-                raise FuchsiaDeviceError(FUCHSIA_INVALID_CONTROL_STATE %
-                                         action)
-            timeout_counter = 0
-            while not process_state:
-                self.log.info(process_initial_msg)
-                time.sleep(DAEMON_INIT_TIMEOUT_SEC)
-                timeout_counter += 1
-                process_state = (self.check_process_with_expectation(
-                    process_name, expectation=action))
-                if timeout_counter == (DAEMON_INIT_TIMEOUT_SEC * 3):
-                    self.log.info(process_timeout_msg)
-                    break
-            if not process_state:
-                raise FuchsiaDeviceError(FUCHSIA_COULD_NOT_GET_DESIRED_STATE %
-                                         (action, process_name))
-        except Exception as e:
-            self.log.info(unable_to_connect_msg)
-            raise e
-        finally:
-            if action == 'stop' and (process_name == 'sl4f'
-                                     or process_name == 'sl4f.cmx'):
-                self._persistent_ssh_conn.close()
-                self._persistent_ssh_conn = None
+                self.log.debug(
+                    'Device does not yet have an ipv4 address...retrying in 1 '
+                    'second.')
+                time.sleep(1)
+        else:
+            raise ConnectionError('Device failed to get an ipv4 address.')
+
+    def wait_for_ipv6_addr(self, interface: str) -> None:
+        """Checks if device has an ipv6 private local address. Sleeps 1 second
+        between retries.
+
+        Args:
+            interface: name of interface from which to get ipv6 address.
+
+        Raises:
+            ConnectionError, if device does not have an ipv6 address after all
+            timeout.
+        """
+        self.log.info(
+            f'Checking for valid ipv6 addr. Retry {IP_ADDRESS_TIMEOUT} seconds.'
+        )
+        timeout = time.time() + IP_ADDRESS_TIMEOUT
+        while time.time() < timeout:
+            ip_addrs = self.get_interface_ip_addresses(interface)
+            if len(ip_addrs['ipv6_private_local']) > 0:
+                self.log.info("Device has an ipv6 private local address: "
+                              f"{ip_addrs['ipv6_private_local'][0]}")
+                break
+            else:
+                self.log.debug(
+                    'Device does not yet have an ipv6 address...retrying in 1 '
+                    'second.')
+                time.sleep(1)
+        else:
+            raise ConnectionError('Device failed to get an ipv6 address.')
+
+    def _check_reachable(self,
+                         timeout_sec: int = FUCHSIA_DEFAULT_CONNECT_TIMEOUT
+                         ) -> None:
+        """Checks the reachability of the Fuchsia device."""
+        end_time = time.time() + timeout_sec
+        self.log.info('Verifying device is reachable.')
+        while time.time() < end_time:
+            # TODO (b/249343632): Consolidate ping commands and fix timeout in
+            # utils.can_ping.
+            if utils.can_ping(job, self.ip):
+                self.log.info('Device is reachable.')
+                break
+            else:
+                self.log.debug(
+                    'Device is not reachable. Retrying in 1 second.')
+                time.sleep(1)
+        else:
+            raise ConnectionError('Device is unreachable.')
+
+    def _check_unreachable(self,
+                           timeout_sec: int = FUCHSIA_DEFAULT_CONNECT_TIMEOUT
+                           ) -> None:
+        """Checks the Fuchsia device becomes unreachable."""
+        end_time = time.time() + timeout_sec
+        self.log.info('Verifying device is unreachable.')
+        while (time.time() < end_time):
+            if utils.can_ping(job, self.ip):
+                self.log.debug(
+                    'Device is still reachable. Retrying in 1 second.')
+                time.sleep(1)
+            else:
+                self.log.info('Device is not reachable.')
+                break
+        else:
+            raise ConnectionError('Device failed to become unreachable.')
 
     def check_connect_response(self, connect_response):
         if connect_response.get("error") is None:
@@ -1138,7 +875,7 @@
             # Country code can be None, from ACTS config.
             if desired_country_code:
                 desired_country_code = desired_country_code.upper()
-                response = self.regulatory_region_lib.setRegion(
+                response = self.sl4f.regulatory_region_lib.setRegion(
                     desired_country_code)
                 if response.get('error'):
                     raise FuchsiaDeviceError(
@@ -1146,7 +883,8 @@
                         response['error'])
                 end_time = time.time() + FUCHSIA_COUNTRY_CODE_TIMEOUT
                 while time.time() < end_time:
-                    ascii_cc = self.wlan_lib.wlanGetCountry(0).get('result')
+                    ascii_cc = self.sl4f.wlan_lib.wlanGetCountry(0).get(
+                        'result')
                     # Convert ascii_cc to string, then compare
                     if ascii_cc and (''.join(chr(c) for c in ascii_cc).upper()
                                      == desired_country_code):
@@ -1158,100 +896,31 @@
                 raise FuchsiaDeviceError('Country code never updated to %s' %
                                          desired_country_code)
 
-    @backoff.on_exception(backoff.constant,
-                          (FuchsiaSyslogError, socket.timeout),
-                          interval=1.5,
-                          max_tries=4)
-    def start_services(self):
-        """Starts long running services on the Fuchsia device.
-
-        Starts a syslog streaming process, SL4F server, initializes a connection
-        to the SL4F server, then starts an isolated ffx daemon.
-
-        """
-        self.log.debug("Attempting to start Fuchsia device services on %s." %
-                       self.ip)
-        if self.ssh_config:
-            self.log_process = create_syslog_process(self.serial,
-                                                     self.log_path,
-                                                     self.ip,
-                                                     self.ssh_username,
-                                                     self.ssh_config,
-                                                     ssh_port=self.ssh_port)
-
-            try:
-                self.log_process.start()
-            except FuchsiaSyslogError as e:
-                # Before backing off and retrying, stop the syslog if it
-                # failed to setup correctly, to prevent threading error when
-                # retrying
-                self.log_process.stop()
-                raise
-
-            self.control_daemon("sl4f.cmx", "start")
-            self.init_sl4f_connection()
-
-            out_name = "fuchsia_device_%s_%s.txt" % (self.serial, 'fw_version')
-            full_out_path = os.path.join(self.log_path, out_name)
-            fw_file = open(full_out_path, 'w')
-            fw_file.write('%s\n' % self.version())
-            fw_file.close()
-
-        self.init_ffx_connection()
-
     def stop_services(self):
-        """Stops long running services on the fuchsia device.
-
-        Terminates the syslog streaming process, the SL4F server on the device,
-        and the ffx daemon.
-        """
-        self.log.debug("Attempting to stop Fuchsia device services on %s." %
-                       self.ip)
-        if hasattr(self, 'ffx'):
-            self.ffx.clean_up()
-        if self.ssh_config:
-            try:
-                self.control_daemon("sl4f.cmx", "stop")
-            except Exception as err:
-                self.log.exception("Failed to stop sl4f.cmx with: %s" % err)
-            if self.log_process:
-                self.log_process.stop()
+        """Stops the ffx daemon and deletes SL4F property."""
+        self.log.info('Stopping host device services.')
+        del self.sl4f
+        del self.ffx
 
     def load_config(self, config):
         pass
 
-    def take_bug_report(self,
-                        test_name=None,
-                        begin_time=None,
-                        additional_log_objects=None):
+    def take_bug_report(self, test_name=None, begin_time=None):
         """Takes a bug report on the device and stores it in a file.
 
         Args:
-            test_name: Name of the test case that triggered this bug report.
-            begin_time: Epoch time when the test started. If not specified, the
-                current time will be used.
-            additional_log_objects: A list of additional objects in Fuchsia to
-                query in the bug report.  Must be in the following format:
-                /hub/c/scenic.cmx/[0-9]*/out/objects
+            test_name: DEPRECATED. Do not specify this argument; it is only used
+                for logging. Name of the test case that triggered this bug
+                report.
+            begin_time: DEPRECATED. Do not specify this argument; it allows
+                overwriting of bug reports when this function is called several
+                times in one test. Epoch time when the test started. If not
+                specified, the current time will be used.
         """
-        if not additional_log_objects:
-            additional_log_objects = []
-        log_items = []
-        matching_log_items = FUCHSIA_DEFAULT_LOG_ITEMS
-        for additional_log_object in additional_log_objects:
-            if additional_log_object not in matching_log_items:
-                matching_log_items.append(additional_log_object)
-        sn_path = context.get_current_context().get_full_output_path()
-        os.makedirs(sn_path, exist_ok=True)
-
-        epoch = begin_time if begin_time else utils.get_current_epoch_time()
-        time_stamp = acts_logger.normalize_log_line_timestamp(
-            acts_logger.epoch_to_log_line_timestamp(epoch))
-        out_name = f"{self.mdns_name}_{time_stamp}"
-        snapshot_out_name = f"{out_name}.zip"
-        out_name = "%s.txt" % out_name
-        full_out_path = os.path.join(sn_path, out_name)
-        full_sn_out_path = os.path.join(sn_path, snapshot_out_name)
+        if not self.ssh_config:
+            self.log.warn(
+                'Skipping take_bug_report because ssh_config is not specified')
+            return
 
         if test_name:
             self.log.info(
@@ -1259,30 +928,19 @@
         else:
             self.log.info(f"Taking snapshot of {self.mdns_name}")
 
-        if self.ssh_config is not None:
-            try:
-                subprocess.run([
-                    f"ssh -F {self.ssh_config} {self.ip} snapshot > {full_sn_out_path}"
-                ],
-                               shell=True)
-                self.log.info("Snapshot saved at: {}".format(full_sn_out_path))
-            except Exception as err:
-                self.log.error("Failed to take snapshot with: {}".format(err))
+        epoch = begin_time if begin_time else utils.get_current_epoch_time()
+        time_stamp = acts_logger.normalize_log_line_timestamp(
+            acts_logger.epoch_to_log_line_timestamp(epoch))
+        out_dir = context.get_current_context().get_full_output_path()
+        out_path = os.path.join(out_dir, f'{self.mdns_name}_{time_stamp}.zip')
 
-        system_objects = self.send_command_ssh('iquery --find /hub').stdout
-        system_objects = system_objects.split()
-
-        for matching_log_item in matching_log_items:
-            for system_object in system_objects:
-                if re.match(matching_log_item, system_object):
-                    log_items.append(system_object)
-
-        log_command = '%s %s' % (FUCHSIA_DEFAULT_LOG_CMD, ' '.join(log_items))
-        bug_report_data = self.send_command_ssh(log_command).stdout
-
-        bug_report_file = open(full_out_path, 'w')
-        bug_report_file.write(bug_report_data)
-        bug_report_file.close()
+        try:
+            subprocess.run(
+                [f"ssh -F {self.ssh_config} {self.ip} snapshot > {out_path}"],
+                shell=True)
+            self.log.info(f'Snapshot saved to {out_path}')
+        except Exception as err:
+            self.log.error(f'Failed to take snapshot: {err}')
 
     def take_bt_snoop_log(self, custom_name=None):
         """Takes a the bt-snoop log from the device and stores it in a file
@@ -1299,14 +957,7 @@
         else:
             out_name = "%s.pcap" % out_name
         full_out_path = os.path.join(bt_snoop_path, out_name)
-        bt_snoop_data = self.send_command_ssh(
-            'bt-snoop-cli -d -f pcap').raw_stdout
+        bt_snoop_data = self.ssh.run('bt-snoop-cli -d -f pcap').raw_stdout
         bt_snoop_file = open(full_out_path, 'wb')
         bt_snoop_file.write(bt_snoop_data)
         bt_snoop_file.close()
-
-
-class FuchsiaDeviceLoggerAdapter(logging.LoggerAdapter):
-    def process(self, msg, kwargs):
-        msg = "[FuchsiaDevice|%s] %s" % (self.extra["ip"], msg)
-        return msg, kwargs
diff --git a/acts/framework/acts/controllers/fuchsia_lib/audio_lib.py b/acts/framework/acts/controllers/fuchsia_lib/audio_lib.py
index 753f4a2..748f3cd 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/audio_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/audio_lib.py
@@ -21,11 +21,9 @@
 
 
 class FuchsiaAudioLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-        self.log = logger.create_tagged_trace_logger('FuchsiaAudioLib')
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "audio")
 
     def startOutputSave(self):
         """Starts saving audio output on the device
@@ -35,10 +33,8 @@
         """
         test_cmd = "audio_facade.StartOutputSave"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def stopOutputSave(self):
         """Stops saving audio output on the device
@@ -48,10 +44,8 @@
         """
         test_cmd = "audio_facade.StopOutputSave"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def getOutputAudio(self, save_path):
         """Gets the saved audio in base64 encoding. Use base64.b64decode.
@@ -64,10 +58,8 @@
         """
         test_cmd = "audio_facade.GetOutputAudio"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        result = self.send_command(test_id, test_cmd, test_args)
+        result = self.send_command(test_cmd, test_args)
         if result.get("error") is not None:
             self.log.error("Failed to get recorded audio.")
             return False
diff --git a/acts/framework/acts/controllers/fuchsia_lib/backlight_lib.py b/acts/framework/acts/controllers/fuchsia_lib/backlight_lib.py
deleted file mode 100644
index b2ba2bc..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/backlight_lib.py
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright 2020 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-import datetime
-
-from acts.controllers.fuchsia_lib.base_lib import BaseLib
-
-
-class FuchsiaBacklightLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-
-    def getStateNormalized(self):
-        """Gets the backlight state and normalized brightness.
-
-        Returns:
-          The backlight state as a bool and the normalized brightness as a float in [0.0, 1.0], or
-          an error if the call failed.
-        """
-        test_cmd = 'backlight_facade.GetStateNormalized'
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def setStateNormalized(self, backlight_on, brightness):
-        """Sets the backlight state and normalized brightness.
-
-        Args:
-          backlight_on: A bool indicating whether or not the backlight is on.
-          brightness: A float in [0.0, 1.0] representing the backlight brightness.
-
-        Returns:
-          None or an error if the call failed.
-        """
-        test_cmd = 'backlight_facade.SetStateNormalized'
-        test_args = {'backlight_on': backlight_on, 'brightness': brightness}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getNormalizedBrightnessScale(self):
-        """Gets the normalized brightness scale.
-
-        Returns:
-          The normalized brightness scale as a float in [0.0, 1.0], or an error if the call failed.
-        """
-        test_cmd = 'backlight_facade.GetNormalizedBrightnessScale'
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def setNormalizedBrightnessScale(self, scale):
-        """Sets the normalized brightness scale.
-
-        Args:
-          scale: The normalized brightness scale to set as a float in [0.0, 1.0].
-
-        Returns:
-          None or an error if the call failed.
-        """
-        test_cmd = 'backlight_facade.SetNormalizedBrightnessScale'
-        test_args = scale
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/base_lib.py b/acts/framework/acts/controllers/fuchsia_lib/base_lib.py
index 23035fc..d282106 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/base_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/base_lib.py
@@ -14,74 +14,94 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import collections
 import json
-import logging
-import math
-import os
-import random
-import re
-import requests
 import socket
-import time
 
+from typing import Any, Mapping
 from urllib.parse import urlparse
+from urllib.request import Request, urlopen
 
-from acts import utils
+from acts import logger, utils
 from acts.libs.proc import job
 
+DEFAULT_SL4F_RESPONSE_TIMEOUT_SEC = 30
+
 
 class DeviceOffline(Exception):
     """Exception if the device is no longer reachable via the network."""
 
 
+class SL4FCommandFailed(Exception):
+    """A SL4F command to the server failed."""
+
+
 class BaseLib():
-    def __init__(self, addr, tc, client_id):
+
+    def __init__(self, addr: str, logger_tag: str) -> None:
         self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+        self.log = logger.create_tagged_trace_logger(f"SL4F | {self.address} | {logger_tag}")
 
-    def build_id(self, test_id):
-        """Concatenates client_id and test_id to form a command_id.
-
-        Args:
-            test_id: string, unique identifier of test command.
-        """
-        return self.client_id + "." + str(test_id)
-
-    def send_command(self, test_id, test_cmd, test_args, response_timeout=30):
+    def send_command(
+        self,
+        cmd: str,
+        args: Mapping[str, Any],
+        response_timeout: int = DEFAULT_SL4F_RESPONSE_TIMEOUT_SEC
+    ) -> Mapping[str, Any]:
         """Builds and sends a JSON command to SL4F server.
 
         Args:
-            test_id: string, unique identifier of test command.
-            test_cmd: string, sl4f method name of command.
-            test_args: dictionary, arguments required to execute test_cmd.
-            response_timeout: int, seconds to wait for a response before
-                throwing an exception. Defaults to no timeout.
+            cmd: SL4F method name of command.
+            args: Arguments required to execute cmd.
+            response_timeout: Seconds to wait for a response before
+                throwing an exception.
 
         Returns:
-            Dictionary, Result of sl4f command executed.
+            Response from SL4F server.
+
+        Throws:
+            TimeoutError: The HTTP request timed out waiting for a response
         """
-        if not utils.can_ping(job, urlparse(self.address).hostname):
-            raise DeviceOffline("FuchsiaDevice %s is not reachable via the "
-                                "network." % urlparse(self.address).hostname)
-        test_data = json.dumps({
+        data = {
             "jsonrpc": "2.0",
-            "id": test_id,
-            "method": test_cmd,
-            "params": test_args
-        })
+            # id is required by the SL4F server to parse test_data but is not
+            # currently used.
+            "id": "",
+            "method": cmd,
+            "params": args
+        }
+        data_json = json.dumps(data).encode("utf-8")
+        req = Request(self.address,
+                      data=data_json,
+                      headers={
+                          "Content-Type": "application/json; charset=utf-8",
+                          "Content-Length": len(data_json),
+                      })
+
+        self.log.debug(f'Sending request "{cmd}" with {args}')
         try:
-            return requests.get(url=self.address,
-                                data=test_data,
-                                timeout=response_timeout).json()
-        except requests.exceptions.Timeout as e:
-            if not utils.can_ping(job, urlparse(self.address).hostname):
+            response = urlopen(req, timeout=response_timeout)
+        except (TimeoutError, socket.timeout) as e:
+            host = urlparse(self.address).hostname
+            if not utils.can_ping(job, host):
                 raise DeviceOffline(
-                    "FuchsiaDevice %s is not reachable via the "
-                    "network." % urlparse(self.address).hostname)
-            else:
-                logging.debug(
-                    'FuchsiaDevice %s is online but SL4f call timed out.' %
-                    urlparse(self.address).hostname)
-                raise e
+                    f'FuchsiaDevice {host} is not reachable via the network.')
+            if type(e) == socket.timeout:
+                # socket.timeout was aliased to TimeoutError in Python 3.10. For
+                # older versions of Python, we need to cast to TimeoutError to
+                # provide a version-agnostic API.
+                raise TimeoutError("socket timeout") from e
+            raise e
+
+        response_body = response.read().decode("utf-8")
+        try:
+            response_json = json.loads(response_body)
+            self.log.debug(f'Received response for "{cmd}": {response_json}')
+        except json.JSONDecodeError as e:
+            raise SL4FCommandFailed(response_body) from e
+
+        # If the SL4F command fails it returns a str, without an 'error' field
+        # to get.
+        if not isinstance(response_json, dict):
+            raise SL4FCommandFailed(response_json)
+
+        return response_json
diff --git a/acts/framework/acts/controllers/fuchsia_lib/basemgr_lib.py b/acts/framework/acts/controllers/fuchsia_lib/basemgr_lib.py
index 6772d38..9e9f037 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/basemgr_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/basemgr_lib.py
@@ -22,10 +22,9 @@
 
 
 class FuchsiaBasemgrLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "basemgr")
 
     def restartSession(self):
         """Restarts an ongoing basemgr session
@@ -36,10 +35,8 @@
                 result: 'Success', 'NoSessionToRestart', or None if error
         """
         test_cmd = COMMAND_RESTART_SESSION
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def startBasemgr(self):
         """Starts basemgr service
@@ -50,10 +47,8 @@
                 result: 'Success' or None if error
         """
         test_cmd = COMMAND_START_BASEMGR
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def killBasemgr(self):
         """Kill basemgr service, if one is running
@@ -64,7 +59,5 @@
                 result: 'Success', 'NoBasemgrToKill', or None if error
         """
         test_cmd = COMMAND_KILL_BASEMGR
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
diff --git a/acts/framework/acts/controllers/fuchsia_lib/bt/avdtp_lib.py b/acts/framework/acts/controllers/fuchsia_lib/bt/avdtp_lib.py
index fc2a549..e1110f5 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/bt/avdtp_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/bt/avdtp_lib.py
@@ -18,10 +18,9 @@
 
 
 class FuchsiaAvdtpLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "avdtp")
 
     def init(self, initiator_delay=None):
         """Initializes the AVDTP service with optional initiator_delay.
@@ -34,12 +33,9 @@
             Dictionary, None if success, error if error.
         """
         test_cmd = "avdtp_facade.AvdtpInit"
-
         test_args = {"initiator_delay": initiator_delay}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def getConnectedPeers(self):
         """Gets the AVDTP connected peers.
@@ -49,10 +45,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpGetConnectedPeers"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setConfiguration(self, peer_id):
         """Sends the AVDTP command to input peer_id: set configuration
@@ -65,10 +59,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpSetConfiguration"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def getConfiguration(self, peer_id):
         """Sends the AVDTP command to input peer_id: get configuration
@@ -81,10 +73,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpGetConfiguration"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def getCapabilities(self, peer_id):
         """Sends the AVDTP command to input peer_id: get capabilities
@@ -97,10 +87,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpGetCapabilities"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def getAllCapabilities(self, peer_id):
         """Sends the AVDTP command to input peer_id: get all capabilities
@@ -113,10 +101,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpGetAllCapabilities"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def reconfigureStream(self, peer_id):
         """Sends the AVDTP command to input peer_id: reconfigure stream
@@ -129,10 +115,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpReconfigureStream"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def suspendStream(self, peer_id):
         """Sends the AVDTP command to input peer_id: suspend stream
@@ -144,10 +128,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpSuspendStream"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def suspendAndReconfigure(self, peer_id):
         """Sends the AVDTP command to input peer_id: suspend and reconfigure
@@ -160,10 +142,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpSuspendAndReconfigure"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def releaseStream(self, peer_id):
         """Sends the AVDTP command to input peer_id: release stream
@@ -176,10 +156,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpReleaseStream"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def establishStream(self, peer_id):
         """Sends the AVDTP command to input peer_id: establish stream
@@ -192,10 +170,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpEstablishStream"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def startStream(self, peer_id):
         """Sends the AVDTP command to input peer_id: start stream
@@ -208,10 +184,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpStartStream"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def abortStream(self, peer_id):
         """Sends the AVDTP command to input peer_id: abort stream
@@ -227,7 +201,7 @@
         test_id = self.build_id(self.test_counter)
         self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def establishStream(self, peer_id):
         """Sends the AVDTP command to input peer_id: establish stream
@@ -240,10 +214,8 @@
         """
         test_cmd = "avdtp_facade.AvdtpEstablishStream"
         test_args = {"identifier": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def removeService(self):
         """Removes the AVDTP service from the Fuchsia device
@@ -253,7 +225,5 @@
         """
         test_cmd = "avdtp_facade.AvdtpRemoveService"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/bt/ble_lib.py b/acts/framework/acts/controllers/fuchsia_lib/bt/ble_lib.py
index 7a46e07..acf1564 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/bt/ble_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/bt/ble_lib.py
@@ -20,10 +20,9 @@
 
 
 class FuchsiaBleLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "ble")
 
     def _convert_human_readable_uuid_to_byte_list(self, readable_uuid):
         """Converts a readable uuid to a byte list.
@@ -31,7 +30,8 @@
         Args:
             readable_uuid: string, A readable uuid in the format:
                 Input: "00001101-0000-1000-8000-00805f9b34fb"
-                Output: ['fb', '34', '9b', '5f', '80', '00', '00', '80', '00', '10', '00', '00', '01', '11', '00', '00']
+                Output: ['fb', '34', '9b', '5f', '80', '00', '00', '80', '00',
+                         '10', '00', '00', '01', '11', '00', '00']
 
         Returns:
             A byte list representing the readable uuid.
@@ -53,10 +53,8 @@
         """
         test_cmd = "ble_advertise_facade.BleStopAdvertise"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def bleStartBleAdvertising(self,
                                advertising_data,
@@ -112,15 +110,12 @@
             "interval_ms": interval,
             "connectable": connectable
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
-    def blePublishService(self, id_, primary, type_, service_id):
+    def blePublishService(self, primary, type_, service_id):
         """Publishes services specified by input args
 
         Args:
-            id: string, Identifier of service.
             primary: bool, Flag of service.
             type: string, Canonical 8-4-4-4-12 uuid of service.
             service_proxy_key: string, Unique identifier to specify where to publish service
@@ -130,12 +125,9 @@
         """
         test_cmd = "bluetooth.BlePublishService"
         test_args = {
-            "id": id_,
             "primary": primary,
             "type": type_,
             "local_service_id": service_id
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/bt/bts_lib.py b/acts/framework/acts/controllers/fuchsia_lib/bt/bts_lib.py
index 9d4203d..bbb3823 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/bt/bts_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/bt/bts_lib.py
@@ -14,27 +14,14 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import collections
-import json
-import logging
-import math
-import os
-import random
-import re
-import requests
-import socket
-import time
-
 from acts.controllers.fuchsia_lib.base_lib import BaseLib
 
 
 class FuchsiaBtsLib(BaseLib):
     # Class representing the Bluetooth Access Library.
 
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "bt_sys")
 
     def setDiscoverable(self, discoverable):
         """Sets the device to be discoverable over BR/EDR.
@@ -48,10 +35,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothSetDiscoverable"
         test_args = {"discoverable": discoverable}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setName(self, name):
         """Sets the local Bluetooth name of the device.
@@ -64,10 +49,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothSetName"
         test_args = {"name": name}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def inputPairingPin(self, pin):
         """Inputs the pairing pin to the Fuchsia devices' pairing delegate.
@@ -80,10 +63,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothInputPairingPin"
         test_args = {"pin": pin}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def getPairingPin(self):
         """Gets the pairing pin from the Fuchsia devices' pairing delegate.
@@ -93,10 +74,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothGetPairingPin"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def initBluetoothSys(self):
         """Initialises the Bluetooth sys Interface proxy in SL4F.
@@ -106,10 +85,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothInitSys"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def requestDiscovery(self, discovery):
         """Start or stop Bluetooth Control device discovery.
@@ -123,10 +100,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothRequestDiscovery"
         test_args = {"discovery": discovery}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def getKnownRemoteDevices(self):
         """Get known remote BR/EDR and LE devices.
@@ -136,10 +111,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothGetKnownRemoteDevices"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def forgetDevice(self, identifier):
         """Forgets a devices pairing.
@@ -152,10 +125,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothForgetDevice"
         test_args = {"identifier": identifier}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def disconnectDevice(self, identifier):
         """Disconnects a devices.
@@ -168,10 +139,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothDisconnectDevice"
         test_args = {"identifier": identifier}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def connectDevice(self, identifier):
         """Connects to a devices.
@@ -184,10 +153,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothConnectDevice"
         test_args = {"identifier": identifier}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def getActiveAdapterAddress(self):
         """Gets the current Active Adapter's address.
@@ -197,10 +164,8 @@
         """
         test_cmd = "bt_sys_facade.BluetoothGetActiveAdapterAddress"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def pair(self, identifier, pairing_security_level, non_bondable,
              transport):
@@ -231,9 +196,8 @@
             "non_bondable": non_bondable,
             "transport": transport,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-        return self.send_command(test_id, test_cmd, test_args)
+
+        return self.send_command(test_cmd, test_args)
 
     def acceptPairing(self,
                       input_capabilities="NONE",
@@ -259,6 +223,5 @@
             "input": input_capabilities,
             "output": output_capabilities,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-        return self.send_command(test_id, test_cmd, test_args)
+
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/bt/gattc_lib.py b/acts/framework/acts/controllers/fuchsia_lib/bt/gattc_lib.py
index b64ed4d..e9e3f5c 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/bt/gattc_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/bt/gattc_lib.py
@@ -18,10 +18,9 @@
 
 
 class FuchsiaGattcLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "gatt_client")
 
     def bleStartBleScan(self, scan_filter):
         """Starts a BLE scan
@@ -38,10 +37,8 @@
         test_args = {
             "filter": scan_filter,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def bleStopBleScan(self):
         """Stops a BLE scan
@@ -51,10 +48,8 @@
         """
         test_cmd = "gatt_client_facade.BleStopScan"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def listServices(self, id):
         """Lists services of a peripheral specified by id.
@@ -67,10 +62,8 @@
         """
         test_cmd = "gatt_client_facade.GattcListServices"
         test_args = {"identifier": id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def bleGetDiscoveredDevices(self):
         """Stops a BLE scan
@@ -80,10 +73,8 @@
         """
         test_cmd = "gatt_client_facade.BleGetDiscoveredDevices"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def discoverCharacteristics(self):
         """Discover the characteristics of a connected service.
@@ -94,10 +85,8 @@
         """
         test_cmd = "gatt_client_facade.GattcDiscoverCharacteristics"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def writeCharById(self, id, offset, write_value):
         """Write Characteristic by id..
@@ -116,10 +105,8 @@
             "offset": offset,
             "write_value": write_value,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def writeLongCharById(self, id, offset, write_value, reliable_mode=False):
         """Write Characteristic by id.
@@ -140,10 +127,8 @@
             "write_value": write_value,
             "reliable_mode": reliable_mode
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def writeLongDescById(self, id, offset, write_value):
         """Write Descriptor by id.
@@ -162,10 +147,8 @@
             "offset": offset,
             "write_value": write_value,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def writeCharByIdWithoutResponse(self, id, write_value):
         """Write Characteristic by id without response.
@@ -182,10 +165,8 @@
             "identifier": id,
             "write_value": write_value,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def enableNotifyCharacteristic(self, id):
         """Enable notifications on a Characteristic.
@@ -200,10 +181,8 @@
         test_args = {
             "identifier": id,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def disableNotifyCharacteristic(self, id):
         """Disable notifications on a Characteristic.
@@ -219,10 +198,8 @@
             "identifier": id,
             "value": False,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def readCharacteristicById(self, id):
         """Read Characteristic value by id..
@@ -237,10 +214,8 @@
         test_args = {
             "identifier": id,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def readCharacteristicByType(self, uuid):
         """Read Characteristic value by id..
@@ -255,10 +230,8 @@
         test_args = {
             "uuid": uuid,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def readDescriptorById(self, id):
         """Read Descriptor value by id..
@@ -273,10 +246,8 @@
         test_args = {
             "identifier": id,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def readLongDescriptorById(self, id, offset, max_bytes):
         """Reads Long Descriptor value by id.
@@ -295,10 +266,8 @@
             "offset": offset,
             "max_bytes": max_bytes
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def writeDescriptorById(self, id, offset, write_value):
         """Write Descriptor by id.
@@ -315,10 +284,8 @@
             "identifier": id,
             "write_value": write_value,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def readLongCharacteristicById(self, id, offset, max_bytes):
         """Reads Long Characteristic value by id.
@@ -337,10 +304,8 @@
             "offset": offset,
             "max_bytes": max_bytes
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def connectToService(self, id, service_id):
         """ Connect to a specific Service specified by id.
@@ -353,10 +318,8 @@
         """
         test_cmd = "gatt_client_facade.GattcConnectToService"
         test_args = {"identifier": id, "service_identifier": service_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def bleConnectToPeripheral(self, id):
         """Connects to a peripheral specified by id.
@@ -369,10 +332,8 @@
         """
         test_cmd = "gatt_client_facade.BleConnectPeripheral"
         test_args = {"identifier": id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def bleDisconnectPeripheral(self, id):
         """Disconnects from a peripheral specified by id.
@@ -385,7 +346,5 @@
         """
         test_cmd = "gatt_client_facade.BleDisconnectPeripheral"
         test_args = {"identifier": id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
\ No newline at end of file
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/bt/gatts_lib.py b/acts/framework/acts/controllers/fuchsia_lib/bt/gatts_lib.py
index 74dc770..23a983e 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/bt/gatts_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/bt/gatts_lib.py
@@ -18,10 +18,9 @@
 
 
 class FuchsiaGattsLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "gatt_server")
 
     def publishServer(self, database):
         """Publishes services specified by input args
@@ -37,10 +36,8 @@
         test_args = {
             "database": database,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def closeServer(self):
         """Closes an active GATT server.
@@ -50,7 +47,5 @@
         """
         test_cmd = "gatt_server_facade.GattServerCloseServer"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/bt/hfp_lib.py b/acts/framework/acts/controllers/fuchsia_lib/bt/hfp_lib.py
index cd789cf..af51db1 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/bt/hfp_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/bt/hfp_lib.py
@@ -18,10 +18,9 @@
 
 
 class FuchsiaHfpLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "hfp")
 
     def init(self):
         """Initializes the HFP service.
@@ -30,12 +29,9 @@
             Dictionary, None if success, error if error.
         """
         test_cmd = "hfp_facade.HfpInit"
-
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def removeService(self):
         """Removes the HFP service from the Fuchsia device
@@ -45,10 +41,8 @@
         """
         test_cmd = "hfp_facade.HfpRemoveService"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def listPeers(self):
         """List all connected HFP peer devices.
@@ -58,10 +52,8 @@
         """
         test_cmd = "hfp_facade.ListPeers"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setActivePeer(self, peer_id):
         """Set the active HFP peer device. All peer specific commands will be
@@ -76,10 +68,8 @@
         """
         test_cmd = "hfp_facade.SetActivePeer"
         test_args = {"peer_id": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def listCalls(self):
         """List all calls known to the sl4f component.
@@ -89,10 +79,8 @@
         """
         test_cmd = "hfp_facade.ListCalls"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def newCall(self, remote, state, direction):
         """Opens a new call channel and alerts the HFP peer.
@@ -107,10 +95,8 @@
         """
         test_cmd = "hfp_facade.NewCall"
         test_args = {"remote": remote, "state": state, "direction": direction}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def initiateIncomingCall(self, remote):
         """Opens an incoming call channel and alerts the HFP peer.
@@ -123,10 +109,8 @@
         """
         test_cmd = "hfp_facade.IncomingCall"
         test_args = {"remote": remote}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def initiateIncomingWaitingCall(self, remote):
         """Opens an incoming call when there is an onging call and alerts
@@ -139,11 +123,9 @@
             Dictionary, call_id if success, error if error.
         """
         test_cmd = "hfp_facade.IncomingWaitingCall"
-        test_args = {"remote": remote }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
+        test_args = {"remote": remote}
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def initiateOutgoingCall(self, remote):
         """Opens an outgoing call channel and alerts the HFP peer.
@@ -156,10 +138,8 @@
         """
         test_cmd = "hfp_facade.OutgoingCall"
         test_args = {"remote": remote}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setCallActive(self, call_id):
         """Sets the specified call to the "OngoingActive" state.
@@ -172,10 +152,8 @@
         """
         test_cmd = "hfp_facade.SetCallActive"
         test_args = {"call_id": call_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setCallHeld(self, call_id):
         """Sets the specified call to the "OngoingHeld" state.
@@ -188,10 +166,8 @@
         """
         test_cmd = "hfp_facade.SetCallHeld"
         test_args = {"call_id": call_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setCallTerminated(self, call_id):
         """Sets the specified call to the "Terminated" state.
@@ -204,10 +180,8 @@
         """
         test_cmd = "hfp_facade.SetCallTerminated"
         test_args = {"call_id": call_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setCallTransferredToAg(self, call_id):
         """Sets the specified call to the "TransferredToAg" state.
@@ -220,10 +194,8 @@
         """
         test_cmd = "hfp_facade.SetCallTransferredToAg"
         test_args = {"call_id": call_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setSpeakerGain(self, value):
         """Sets the active peer's speaker gain.
@@ -236,10 +208,8 @@
         """
         test_cmd = "hfp_facade.SetSpeakerGain"
         test_args = {"value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setMicrophoneGain(self, value):
         """Sets the active peer's microphone gain.
@@ -252,10 +222,8 @@
         """
         test_cmd = "hfp_facade.SetMicrophoneGain"
         test_args = {"value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setServiceAvailable(self, value):
         """Sets the simulated network service status reported by the call manager.
@@ -268,10 +236,8 @@
         """
         test_cmd = "hfp_facade.SetServiceAvailable"
         test_args = {"value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setRoaming(self, value):
         """Sets the simulated roaming status reported by the call manager.
@@ -284,10 +250,8 @@
         """
         test_cmd = "hfp_facade.SetRoaming"
         test_args = {"value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setSignalStrength(self, value):
         """Sets the simulated signal strength reported by the call manager.
@@ -300,10 +264,8 @@
         """
         test_cmd = "hfp_facade.SetSignalStrength"
         test_args = {"value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setSubscriberNumber(self, value):
         """Sets the subscriber number reported by the call manager.
@@ -316,10 +278,8 @@
         """
         test_cmd = "hfp_facade.SetSubscriberNumber"
         test_args = {"value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setOperator(self, value):
         """Sets the operator value reported by the call manager.
@@ -332,10 +292,8 @@
         """
         test_cmd = "hfp_facade.SetOperator"
         test_args = {"value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setNrecSupport(self, value):
         """Sets the noise reduction/echo cancelation support reported by the call manager.
@@ -348,10 +306,8 @@
         """
         test_cmd = "hfp_facade.SetNrecSupport"
         test_args = {"value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setBatteryLevel(self, value):
         """Sets the battery level reported by the call manager.
@@ -364,10 +320,8 @@
         """
         test_cmd = "hfp_facade.SetBatteryLevel"
         test_args = {"value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setLastDialed(self, number):
         """Sets the last dialed number in the call manager.
@@ -380,10 +334,8 @@
         """
         test_cmd = "hfp_facade.SetLastDialed"
         test_args = {"number": number}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def clearLastDialed(self):
         """Clears the last dialed number in the call manager.
@@ -393,10 +345,8 @@
         """
         test_cmd = "hfp_facade.ClearLastDialed"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setMemoryLocation(self, location, number):
         """Sets a memory location to point to a remote number.
@@ -410,10 +360,8 @@
         """
         test_cmd = "hfp_facade.SetMemoryLocation"
         test_args = {"location": location, "number": number}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def clearMemoryLocation(self, location):
         """Clear a memory location so that it no longer points to a remote
@@ -427,10 +375,8 @@
         """
         test_cmd = "hfp_facade.ClearMemoryLocation"
         test_args = {"location": location}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setDialResult(self, number, status):
         """Sets the status result to be returned when the number is dialed.
@@ -445,10 +391,8 @@
         """
         test_cmd = "hfp_facade.SetDialResult"
         test_args = {"number": number, "status": status}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def getState(self):
         """Get the call manager's state.
@@ -458,10 +402,8 @@
         """
         test_cmd = "hfp_facade.GetState"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def setConnectionBehavior(self, autoconnect):
         """Set the Service Level Connection behavior when a new peer connects.
@@ -474,7 +416,5 @@
         """
         test_cmd = "hfp_facade.SetConnectionBehavior"
         test_args = {"autoconnect": autoconnect}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/bt/rfcomm_lib.py b/acts/framework/acts/controllers/fuchsia_lib/bt/rfcomm_lib.py
index 6cc08b8..5ed9e96 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/bt/rfcomm_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/bt/rfcomm_lib.py
@@ -18,10 +18,9 @@
 
 
 class FuchsiaRfcommLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "rfcomm")
 
     def init(self):
         """Initializes the RFCOMM service.
@@ -30,12 +29,9 @@
             Dictionary, None if success, error if error.
         """
         test_cmd = "rfcomm_facade.RfcommInit"
-
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def removeService(self):
         """Removes the RFCOMM service from the Fuchsia device
@@ -45,10 +41,8 @@
         """
         test_cmd = "rfcomm_facade.RfcommRemoveService"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def disconnectSession(self, peer_id):
         """Closes the RFCOMM Session with the remote peer
@@ -58,10 +52,8 @@
         """
         test_cmd = "rfcomm_facade.DisconnectSession"
         test_args = {"peer_id": peer_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def connectRfcommChannel(self, peer_id, server_channel_number):
         """Makes an outgoing RFCOMM connection to the remote peer
@@ -74,10 +66,8 @@
             "peer_id": peer_id,
             "server_channel_number": server_channel_number
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def disconnectRfcommChannel(self, peer_id, server_channel_number):
         """Closes the RFCOMM channel with the remote peer
@@ -90,10 +80,8 @@
             "peer_id": peer_id,
             "server_channel_number": server_channel_number
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def sendRemoteLineStatus(self, peer_id, server_channel_number):
         """Sends a Remote Line Status update to the remote peer for the provided channel number
@@ -106,10 +94,8 @@
             "peer_id": peer_id,
             "server_channel_number": server_channel_number
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def writeRfcomm(self, peer_id, server_channel_number, data):
         """Sends data to the remote peer over the RFCOMM channel
@@ -123,7 +109,5 @@
             "server_channel_number": server_channel_number,
             "data": data
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/bt/sdp_lib.py b/acts/framework/acts/controllers/fuchsia_lib/bt/sdp_lib.py
index 6f7a62c..f050283 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/bt/sdp_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/bt/sdp_lib.py
@@ -18,10 +18,9 @@
 
 
 class FuchsiaProfileServerLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "profile_server")
 
     def addService(self, record):
         """Publishes an SDP service record specified by input args
@@ -37,10 +36,8 @@
         test_args = {
             "record": record,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def addSearch(self, attribute_list, profile_id):
         """Publishes services specified by input args
@@ -56,10 +53,8 @@
             "attribute_list": attribute_list,
             "profile_id": profile_id
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def removeService(self, service_id):
         """Removes a service.
@@ -75,10 +70,8 @@
         test_args = {
             "service_id": service_id,
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def init(self):
         """Initializes the ProfileServerFacade's proxy object.
@@ -90,10 +83,8 @@
         """
         test_cmd = "profile_server_facade.ProfileServerInit"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def cleanUp(self):
         """Cleans up all objects related to SDP.
@@ -103,10 +94,8 @@
         """
         test_cmd = "profile_server_facade.ProfileServerCleanup"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def connectL2cap(self, identifier, psm, mode):
         """ Sends an outgoing l2cap connection to a connected peer device.
@@ -143,7 +132,5 @@
         """
         test_cmd = "profile_server_facade.ProfileServerConnectL2cap"
         test_args = {"identifier": identifier, "psm": psm, "mode": mode}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/ffx.py b/acts/framework/acts/controllers/fuchsia_lib/ffx.py
index e407141..1746ff8 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/ffx.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/ffx.py
@@ -17,20 +17,37 @@
 import json
 import os
 import tempfile
+import subprocess
+import time
 
 from pathlib import Path
+from typing import Any, MutableMapping, Optional
 
 from acts import context
 from acts import logger
 from acts import signals
 from acts import utils
-from acts.libs.proc import job
 
-FFX_DEFAULT_COMMAND_TIMEOUT = 60
+
+FFX_DEFAULT_COMMAND_TIMEOUT: int = 60
 
 
 class FFXError(signals.TestError):
-    pass
+    """Non-zero error code returned from a ffx command."""
+
+    def __init__(self, command: str,
+                 process: subprocess.CalledProcessError) -> None:
+        self.command = command
+        self.stdout: str = process.stdout.decode('utf-8', errors='replace')
+        self.stderr: str = process.stderr.decode('utf-8', errors='replace')
+        self.exit_status = process.returncode
+
+    def __str__(self) -> str:
+        return f'ffx subcommand "{self.command}" returned {self.exit_status}, stdout: "{self.stdout}", stderr: "{self.stderr}"'
+
+
+class FFXTimeout(signals.TestError):
+    """Timed out running a ffx command."""
 
 
 class FFX:
@@ -39,11 +56,16 @@
     Attributes:
         log: Logger for the device-specific instance of ffx.
         binary_path: Path to the ffx binary.
-        target: mDNS nodename of the default Fuchsia target.
+        mdns_name: mDNS nodename of the default Fuchsia target.
+        ip: IP address of the default Fuchsia target.
         ssh_private_key_path: Path to Fuchsia DUT SSH private key.
     """
 
-    def __init__(self, binary_path, target, ssh_private_key_path=None):
+    def __init__(self,
+                 binary_path: str,
+                 mdns_name: str,
+                 ip: str = None,
+                 ssh_private_key_path: str = None):
         """
         Args:
             binary_path: Path to ffx binary.
@@ -51,18 +73,79 @@
             ssh_private_key_path: Path to SSH private key for talking to the
                 Fuchsia DUT.
         """
-        self.log = logger.create_tagged_trace_logger(f"ffx | {target}")
+        self.log = logger.create_tagged_trace_logger(f"ffx | {mdns_name}")
         self.binary_path = binary_path
-        self.target = target
+        self.mdns_name = mdns_name
+        self.ip = ip
         self.ssh_private_key_path = ssh_private_key_path
 
-        self._config_path = None
+        self._env_config_path: Optional[str] = None
+        self._ssh_auth_sock_path: Optional[str] = None
+        self._overnet_socket_path: Optional[str] = None
+        self._has_been_reachable = False
+        self._has_logged_version = False
+
+    def clean_up(self) -> None:
+        if self._env_config_path:
+            self.run("daemon stop", skip_reachability_check=True)
+        if self._ssh_auth_sock_path:
+            Path(self._ssh_auth_sock_path).unlink(missing_ok=True)
+        if self._overnet_socket_path:
+            Path(self._overnet_socket_path).unlink(missing_ok=True)
+
+        self._env_config_path = None
         self._ssh_auth_sock_path = None
         self._overnet_socket_path = None
         self._has_been_reachable = False
         self._has_logged_version = False
 
-    def _create_isolated_environment(self):
+    def run(self,
+            command: str,
+            timeout_sec: int = FFX_DEFAULT_COMMAND_TIMEOUT,
+            skip_status_code_check: bool = False,
+            skip_reachability_check: bool = False
+            ) -> subprocess.CompletedProcess:
+        """Runs an ffx command.
+
+        Verifies reachability before running, if it hasn't already.
+
+        Args:
+            command: 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.
+            verify_reachable: Whether to verify reachability before running.
+
+        Raises:
+            FFXTimeout: when the command times out.
+            FFXError: when the command returns non-zero and skip_status_code_check is False.
+
+        Returns:
+            The results of the command. Note subprocess.CompletedProcess returns
+            stdout and stderr as a byte-array, not a string. Treat these members
+            as such or convert to a string using bytes.decode('utf-8').
+        """
+        if not self._env_config_path:
+            self._create_isolated_environment()
+        if not self._has_been_reachable and not skip_reachability_check:
+            self.log.info(f'Verifying reachability before running "{command}"')
+            self.verify_reachable()
+
+        self.log.debug(f'Running "{command}".')
+        full_command = f'{self.binary_path} -e {self._env_config_path} {command}'
+
+        try:
+            result = subprocess.run(full_command.split(),
+                                    capture_output=True,
+                                    timeout=timeout_sec,
+                                    check=not skip_status_code_check)
+        except subprocess.CalledProcessError as e:
+            raise FFXError(command, e) from e
+        except subprocess.TimeoutExpired as e:
+            raise FFXTimeout(f'Timed out running "{full_command}"') from e
+
+        return result
+
+    def _create_isolated_environment(self) -> None:
         """ Create a new isolated environment for ffx.
 
         This is needed to avoid overlapping ffx daemons while testing in
@@ -77,7 +160,7 @@
         epoch = utils.get_current_epoch_time()
         time_stamp = logger.normalize_log_line_timestamp(
             logger.epoch_to_log_line_timestamp(epoch))
-        target_dir = os.path.join(root_dir, f'{self.target}_{time_stamp}')
+        target_dir = os.path.join(root_dir, f'{self.mdns_name}_{time_stamp}')
         os.makedirs(target_dir, exist_ok=True)
 
         # Sockets need to be created in a different directory to be guaranteed
@@ -87,9 +170,9 @@
         self._overnet_socket_path = tempfile.mkstemp(
             suffix="overnet_socket")[1]
 
-        config = {
+        config: MutableMapping[str, Any] = {
             "target": {
-                "default": self.target,
+                "default": self.mdns_name,
             },
             # Use user-specific and device-specific locations for sockets.
             # Avoids user permission errors in a multi-user test environment.
@@ -116,19 +199,37 @@
             },
         }
 
+        if self.ip:
+            config["discovery"] = {
+                "mdns": {
+                    "enabled": False,
+                },
+            }
+
         # 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_private_key_path:
             config["ssh"]["priv"] = self.ssh_private_key_path
 
-        self._config_path = os.path.join(target_dir, "ffx_config.json")
-        with open(self._config_path, 'w', encoding="utf-8") as f:
+        config_path = os.path.join(target_dir, "ffx_config.json")
+        with open(config_path, 'w', encoding="utf-8") as f:
             json.dump(config, f, ensure_ascii=False, indent=4)
 
+        env = {
+            "user": config_path,
+            "build": None,
+            "global": None,
+        }
+        self._env_config_path = os.path.join(target_dir, "ffx_env.json")
+        with open(self._env_config_path, 'w', encoding="utf-8") as f:
+            json.dump(env, f, ensure_ascii=False, indent=4)
+
         # The ffx daemon will started automatically when needed. There is no
         # need to start it manually here.
 
-    def verify_reachable(self):
+    def verify_reachable(self,
+                         timeout_sec: int = FFX_DEFAULT_COMMAND_TIMEOUT
+                         ) -> None:
         """Verify the target is reachable via RCS and various services.
 
         Blocks until the device allows for an RCS connection. If the device
@@ -140,20 +241,39 @@
 
         When called for the first time, the versions will be checked for
         compatibility.
+
+        Args:
+            timeout_sec: Seconds to wait for reachability check
+
+        Raises:
+            FFXError: when an unknown error occurs
+            FFXTimeout: when the target is unreachable
         """
-        try:
-            self.run("target wait",
-                     timeout_sec=5,
-                     skip_reachability_check=True)
-        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.run("target wait",
-                     timeout_sec=longer_wait_sec,
-                     skip_reachability_check=True)
+        cmd = "target wait"
+        if self.ip:
+            # `target add` does what `target wait` does but adds an entry
+            # to ensure connections can happen without mDNS.
+            # TODO(https://fxbug.dev/105530): Update manual target parsing in
+            # ffx.
+            cmd = f"target add {self.ip}"
+
+        timeout = time.perf_counter() + timeout_sec
+        while True:
+            try:
+                self.run(cmd, timeout_sec=5, skip_reachability_check=True)
+                break
+            except FFXError as e:
+                if 'took too long connecting to ascendd socket' in e.stderr:
+                    err = e
+                else:
+                    raise e
+            except FFXTimeout as e:
+                err = e
+
+            if time.perf_counter() > timeout:
+                raise FFXTimeout(
+                    f'Waited over {timeout_sec}s for ffx to become reachable'
+                ) from err
 
         # Use a shorter timeout than default because device information
         # gathering can hang for a long time if the device is not actually
@@ -174,12 +294,13 @@
             self._has_logged_version = True
             self.compare_version(result)
 
-    def compare_version(self, target_show_result):
+    def compare_version(
+            self, target_show_result: subprocess.CompletedProcess) -> None:
         """Compares the version of Fuchsia with the version of ffx.
 
         Args:
-            target_show_result: job.Result, result of the target show command
-                with JSON output mode enabled
+            target_show_result: Result of the target show command with JSON
+                output mode enabled
         """
         result_json = json.loads(target_show_result.stdout)
         build_info = next(
@@ -187,7 +308,7 @@
         version_info = next(
             filter(lambda s: s.get('label') == 'version', build_info['child']))
         device_version = version_info.get('value')
-        ffx_version = self.run("version").stdout
+        ffx_version = self.run("version").stdout.decode('utf-8')
 
         self.log.info(
             f"Device version: {device_version}, ffx version: {ffx_version}")
@@ -196,67 +317,3 @@
                 "ffx versions that differ from device versions may" +
                 " have compatibility issues. It is recommended to" +
                 " use versions within 6 weeks of each other.")
-
-    def clean_up(self):
-        if self._config_path:
-            self.run("daemon stop", skip_reachability_check=True)
-        if self._ssh_auth_sock_path:
-            Path(self._ssh_auth_sock_path).unlink(missing_ok=True)
-        if self._overnet_socket_path:
-            Path(self._overnet_socket_path).unlink(missing_ok=True)
-
-        self._config_path = None
-        self._ssh_auth_sock_path = None
-        self._overnet_socket_path = None
-        self._has_been_reachable = False
-        self._has_logged_version = False
-
-    def run(self,
-            command,
-            timeout_sec=FFX_DEFAULT_COMMAND_TIMEOUT,
-            skip_status_code_check=False,
-            skip_reachability_check=False):
-        """Runs an ffx command.
-
-        Verifies reachability before running, if it hasn't already.
-
-        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.
-            verify_reachable: Whether to verify reachability before running.
-
-        Raises:
-            job.TimeoutError: when the command times out.
-            Error: when the command returns non-zero and skip_status_code_check is False.
-            FFXError: 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._config_path:
-            self._create_isolated_environment()
-        if not self._has_been_reachable and not skip_reachability_check:
-            self.log.info(f'Verifying reachability before running "{command}"')
-            self.verify_reachable()
-
-        self.log.debug(f'Running "{command}".')
-
-        full_command = f'{self.binary_path} -c {self._config_path} {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 FFXError(
-                f'Error when running "{full_command}": {result.stderr}')
-
-        return result
diff --git a/acts/framework/acts/controllers/fuchsia_lib/gpio_lib.py b/acts/framework/acts/controllers/fuchsia_lib/gpio_lib.py
deleted file mode 100644
index cd4c75d..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/gpio_lib.py
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright 2020 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-from acts.controllers.fuchsia_lib.base_lib import BaseLib
-
-
-class FuchsiaGpioLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-
-    def configIn(self, pin, flags):
-        """ Configures a GPIO for input.
-
-        Args:
-           pin: uint32, pin number of GPIO
-           flags: uint32, flags to configure for GPIO
-
-        Returns:
-           None if success, prints error message if error.
-        """
-        test_cmd = "gpio_facade.ConfigIn"
-        test_args = {"pin": pin, "flags": flags}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def configOut(self, pin, value):
-        """ Configures a GPIO for output.
-
-        Args:
-           pin: uint32, pin number of GPIO
-           value: uint8, initial value
-
-        Returns:
-           None if success, prints error message if error.
-        """
-        test_cmd = "gpio_facade.ConfigOut"
-        test_args = {"pin": pin, "value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def read(self, pin):
-        """ Reads the current value of a GPIO (0 or 1).
-
-        Args:
-           pin: uint32, pin number of GPIO
-
-        Returns:
-           uint8. Current value of GPIO.
-        """
-        test_cmd = "gpio_facade.Read"
-        test_args = {"pin": pin}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def write(self, pin, value):
-        """ Sets the current value of the GPIO (any non-zero value maps to 1).
-
-        Args:
-           pin: uint32, pin number of GPIO
-           value: uint8, value of GPIO
-
-        Returns:
-           None if success, prints error message if error.
-        """
-        test_cmd = "gpio_facade.Write"
-        test_args = {"pin": pin, "value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/hardware_power_statecontrol_lib.py b/acts/framework/acts/controllers/fuchsia_lib/hardware_power_statecontrol_lib.py
index 2663a98..d57bf78 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/hardware_power_statecontrol_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/hardware_power_statecontrol_lib.py
@@ -14,9 +14,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import datetime
+import logging
 import http
-import requests
 
 import acts.controllers.fuchsia_lib.base_lib as base_lib
 
@@ -24,10 +23,20 @@
 
 
 class FuchsiaHardwarePowerStatecontrolLib(base_lib.BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "hardware_power_statecontrol")
+
+    def send_command(self, test_cmd, test_args, response_timeout=30):
+        """Wrap send_command to allow disconnects after sending the request."""
+        try:
+            response = super().send_command(test_cmd, test_args,
+                                            response_timeout)
+        except (TimeoutError, http.client.RemoteDisconnected,
+                base_lib.DeviceOffline) as e:
+            logging.warn(f'Error while sending power command: {e}')
+            return
+        return response
 
     def suspendReboot(self, timeout=HW_PWR_STATE_CONTROL_TIMEOUT):
         """Call Suspend Reboot.
@@ -37,18 +46,7 @@
         """
         test_cmd = "hardware_power_statecontrol_facade.SuspendReboot"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-        try:
-            response = self.send_command(test_id,
-                                         test_cmd,
-                                         test_args,
-                                         response_timeout=timeout)
-        except (ConnectionResetError, base_lib.DeviceOffline,
-                requests.exceptions.ConnectionError,
-                requests.exceptions.ReadTimeout):
-            return
-        return response
+        return self.send_command(test_cmd, test_args, response_timeout=timeout)
 
     def suspendRebootBootloader(self, timeout=HW_PWR_STATE_CONTROL_TIMEOUT):
         """Call Suspend Reboot Bootloader
@@ -58,17 +56,7 @@
         """
         test_cmd = "hardware_power_statecontrol_facade.SuspendRebootBootloader"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-        try:
-            response = self.send_command(test_id,
-                                         test_cmd,
-                                         test_args,
-                                         response_timeout=timeout)
-        except (requests.exceptions.ReadTimeout,
-                http.client.RemoteDisconnected, base_lib.DeviceOffline):
-            return
-        return response
+        return self.send_command(test_cmd, test_args, response_timeout=timeout)
 
     def suspendPoweroff(self, timeout=HW_PWR_STATE_CONTROL_TIMEOUT):
         """Call Suspend Poweroff
@@ -78,17 +66,7 @@
         """
         test_cmd = "hardware_power_statecontrol_facade.SuspendPoweroff"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-        try:
-            response = self.send_command(test_id,
-                                         test_cmd,
-                                         test_args,
-                                         response_timeout=timeout)
-        except (requests.exceptions.ReadTimeout,
-                http.client.RemoteDisconnected, base_lib.DeviceOffline):
-            return
-        return response
+        return self.send_command(test_cmd, test_args, response_timeout=timeout)
 
     def suspendMexec(self, timeout=HW_PWR_STATE_CONTROL_TIMEOUT):
         """Call Suspend Mexec
@@ -98,17 +76,7 @@
         """
         test_cmd = "hardware_power_statecontrol_facade.SuspendMexec"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-        try:
-            response = self.send_command(test_id,
-                                         test_cmd,
-                                         test_args,
-                                         response_timeout=timeout)
-        except (requests.exceptions.ReadTimeout,
-                http.client.RemoteDisconnected, base_lib.DeviceOffline):
-            return
-        return response
+        return self.send_command(test_cmd, test_args, response_timeout=timeout)
 
     def suspendRam(self, timeout=HW_PWR_STATE_CONTROL_TIMEOUT):
         """Call Suspend Ram
@@ -118,14 +86,4 @@
         """
         test_cmd = "hardware_power_statecontrol_facade.SuspendRam"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-        try:
-            response = self.send_command(test_id,
-                                         test_cmd,
-                                         test_args,
-                                         response_timeout=timeout)
-        except (requests.exceptions.ReadTimeout,
-                http.client.RemoteDisconnected, base_lib.DeviceOffline):
-            return
-        return response
+        return self.send_command(test_cmd, test_args, response_timeout=timeout)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/hwinfo_lib.py b/acts/framework/acts/controllers/fuchsia_lib/hwinfo_lib.py
deleted file mode 100644
index 0868fd1..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/hwinfo_lib.py
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright 2020 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-import datetime
-
-from acts.controllers.fuchsia_lib.base_lib import BaseLib
-
-
-class FuchsiaHwinfoLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-
-    def getDeviceInfo(self):
-        """Get's the hw info of the Fuchsia device under test.
-
-        Returns:
-            Dictionary, None if success, error if error.
-        """
-        test_cmd = "hwinfo_facade.HwinfoGetDeviceInfo"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getProductInfo(self):
-        """Get's the hw info of the Fuchsia device under test.
-
-        Returns:
-            Dictionary, None if success, error if error.
-        """
-        test_cmd = "hwinfo_facade.HwinfoGetProductInfo"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getBoardInfo(self):
-        """Get's the hw info of the Fuchsia device under test.
-
-        Returns:
-            Dictionary, None if success, error if error.
-        """
-        test_cmd = "hwinfo_facade.HwinfoGetBoardInfo"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/i2c_lib.py b/acts/framework/acts/controllers/fuchsia_lib/i2c_lib.py
deleted file mode 100644
index 739e6e8..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/i2c_lib.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright 2020 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-from acts.controllers.fuchsia_lib.base_lib import BaseLib
-
-
-class FuchsiaI2cLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-
-    def transfer(self, device_idx, segments_is_write, write_segments_data,
-                 read_segments_length):
-        """Gets the fuchsia.input.report.DeviceDescriptor for a given device.
-
-        Args:
-          device_idx: the integer device index to use, e.g. 6 for /dev/class/i2c/006.
-          segments_is_write: a list of bools specifying whether each segment is a read or a write.
-          write_segments_data: a list of write segments, where each segment is a list of bytes to write.
-          read_segments_length: a list of integers specifying the number of bytes in each read segment.
-
-        Returns:
-          The list of read segments received, or an error message if an error was encountered.
-        """
-        test_cmd = "i2c_facade.Transfer"
-        test_args = {
-            "device_idx": device_idx,
-            "segments_is_write": segments_is_write,
-            "write_segments_data": write_segments_data,
-            "read_segments_length": read_segments_length
-        }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/input_report_lib.py b/acts/framework/acts/controllers/fuchsia_lib/input_report_lib.py
deleted file mode 100644
index aa12321..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/input_report_lib.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright 2020 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-from acts.controllers.fuchsia_lib.base_lib import BaseLib
-
-
-class FuchsiaInputReportLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-
-    """Gets the fuchsia.input.report.DeviceDescriptor for a given device.
-
-    Args:
-      vendor_id: the uint32 vendor ID to match against available input devices, or None to match any
-        vendor ID.
-      product_id: the uint32 product ID to match against available input devices, or None to match
-        any product ID.
-      version: the uint32 version to match against available input devices, or None to match any
-        version.
-
-    Returns:
-      The DeviceDescriptor object, or an error message if an error was encountered.
-    """
-
-    def getDescriptor(self, vendor_id=None, product_id=None, version=None):
-        test_cmd = 'input_report_facade.GetDescriptor'
-        test_args = {
-            'vendor_id': vendor_id,
-            'product_id': product_id,
-            'version': version
-        }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-        return self.send_command(test_id, test_cmd, test_args)
-
-    """Gets the list of fuchsia.input.report.InputReports that were seen since the last call.
-
-    Args:
-      vendor_id: the uint32 vendor ID to match against available input devices, or None to match any
-        vendor ID.
-      product_id: the uint32 product ID to match against available input devices, or None to match
-        any product ID.
-      version: the uint32 version to match against available input devices, or None to match any
-        version.
-
-    Returns:
-      A list of InputReports, or an error message if an error was encountered.
-    """
-
-    def getReports(self, vendor_id=None, product_id=None, version=None):
-        test_cmd = 'input_report_facade.GetReports'
-        test_args = {
-            'vendor_id': vendor_id,
-            'product_id': product_id,
-            'version': version
-        }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-        return self.send_command(test_id, test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/kernel_lib.py b/acts/framework/acts/controllers/fuchsia_lib/kernel_lib.py
deleted file mode 100644
index 210adf4..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/kernel_lib.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2020 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-from acts.controllers.fuchsia_lib.base_lib import BaseLib
-
-
-class FuchsiaKernelLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-
-    def getMemoryStats(self):
-        """ Gets memory stats.
-
-        Returns:
-            MemoryStats struct, prints an error message if error.
-        """
-        test_cmd = "kernel_facade.GetMemoryStats"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getCpuStats(self):
-        """ Gets CPU stats.
-
-        Returns:
-            CpuStats struct, prints an error message if error.
-        """
-        test_cmd = "kernel_facade.GetCpuStats"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getAllStats(self):
-        """ Gets memory and CPU stats.
-
-        Returns:
-            MemoryStats struct and CpuStats struct, prints an error message if error.
-        """
-        test_cmd = "kernel_facade.GetAllStats"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/netstack_controller.py b/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/netstack_controller.py
index e7ca026..3fab8bc 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/netstack_controller.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/lib_controllers/netstack_controller.py
@@ -37,7 +37,7 @@
             List of dicts, one for each interface, containing interface
             information
         """
-        response = self.device.netstack_lib.netstackListInterfaces()
+        response = self.device.sl4f.netstack_lib.netstackListInterfaces()
         if response.get('error'):
             raise NetstackControllerError(
                 'Failed to get network interfaces list: %s' %
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 d50f726..5b99fc3 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
@@ -72,7 +72,7 @@
         """
 
         # Retrieve WLAN interface IDs
-        response = self.device.wlan_lib.wlanGetIfaceIdList()
+        response = self.device.sl4f.wlan_lib.wlanGetIfaceIdList()
         if response.get('error'):
             raise WlanControllerError('Failed to get WLAN iface ids: %s' %
                                       response['error'])
@@ -84,7 +84,7 @@
         # Use IDs to get WLAN interface info and mac addresses
         wlan_ifaces_by_mac = {}
         for id in wlan_iface_ids:
-            response = self.device.wlan_lib.wlanQueryInterface(id)
+            response = self.device.sl4f.wlan_lib.wlanQueryInterface(id)
             if response.get('error'):
                 raise WlanControllerError(
                     'Failed to query wlan iface id %s: %s' %
@@ -139,7 +139,7 @@
             ConnectionError - failure to query PHYs
         """
         self.log.info('Setting DUT country code to %s' % country_code)
-        country_code_response = self.device.regulatory_region_lib.setRegion(
+        country_code_response = self.device.sl4f.regulatory_region_lib.setRegion(
             country_code)
         if country_code_response.get('error'):
             raise EnvironmentError(
@@ -148,7 +148,7 @@
 
         self.log.info('Verifying DUT country code was correctly set to %s.' %
                       country_code)
-        phy_ids_response = self.device.wlan_lib.wlanPhyIdList()
+        phy_ids_response = self.device.sl4f.wlan_lib.wlanPhyIdList()
         if phy_ids_response.get('error'):
             raise ConnectionError('Failed to get phy ids from DUT. Error: %s' %
                                   (country_code, phy_ids_response['error']))
@@ -156,7 +156,8 @@
         end_time = time.time() + TIME_TO_WAIT_FOR_COUNTRY_CODE
         while time.time() < end_time:
             for id in phy_ids_response['result']:
-                get_country_response = self.device.wlan_lib.wlanGetCountry(id)
+                get_country_response = self.device.sl4f.wlan_lib.wlanGetCountry(
+                    id)
                 if get_country_response.get('error'):
                     raise ConnectionError(
                         'Failed to query PHY ID (%s) for country. Error: %s' %
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 53c5839..133cde3 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
@@ -14,15 +14,14 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import requests
+import subprocess
 import time
 
 from acts import logger
 from acts import signals
 
-import typing
-if typing.TYPE_CHECKING:
-    from acts.controllers.fuchsia_device import FuchsiaDevice
+from acts.controllers.fuchsia_lib.ffx import FFX, FFXError, FFXTimeout
+from acts.controllers.fuchsia_lib.sl4f import SL4F
 
 SAVED_NETWORKS = "saved_networks"
 CLIENT_STATE = "client_connections_state"
@@ -34,6 +33,8 @@
 STATE_DISCONNECTED = 'Disconnected'
 STATE_CONNECTION_STOPPED = 'ConnectionStopped'
 
+FUCHSIA_DEFAULT_WLAN_CONFIGURE_TIMEOUT = 30
+
 
 class WlanPolicyControllerError(signals.ControllerError):
     pass
@@ -44,27 +45,33 @@
     FuchsiaDevice object.
     """
 
-    def __init__(self, fuchsia_device):
-        self.device: FuchsiaDevice = fuchsia_device
-        self.log = logger.create_tagged_trace_logger(
-            'WlanPolicyController for FuchsiaDevice | %s' % self.device.ip)
+    def __init__(self, sl4f: SL4F, ffx: FFX):
         self.client_controller = False
         self.preserved_networks_and_client_state = None
         self.policy_configured = False
-        self._paused_session = False
+        self.sl4f = sl4f
+        self.ffx = ffx
+        self.log = logger.create_tagged_trace_logger(
+            f'WlanPolicyController | {ffx.ip}')
 
-    def _configure_wlan(self, preserve_saved_networks, timeout=15):
+    # TODO(b/231252355): Lower default timeout to 15s once ffx becomes more
+    # performant and/or reliable.
+    def configure_wlan(
+            self,
+            preserve_saved_networks: bool,
+            timeout_sec: int = FUCHSIA_DEFAULT_WLAN_CONFIGURE_TIMEOUT) -> None:
         """Sets up wlan policy layer.
 
         Args:
-            preserve_saved_networks: bool, whether to clear existing saved
+            preserve_saved_networks: whether to clear existing saved
                 networks and client state, to be restored at test close.
+            timeout: time to wait for device to configure WLAN.
         """
-        end_time = time.time() + timeout
+        end_time_sec = time.time() + timeout_sec
 
         # Kill basemgr (Component v1 version of session manager)
-        while time.time() < end_time:
-            response = self.device.basemgr_lib.killBasemgr()
+        while time.time() < end_time_sec:
+            response = self.sl4f.basemgr_lib.killBasemgr()
             if not response.get('error'):
                 self.log.debug('Basemgr kill call successfully issued.')
                 break
@@ -75,20 +82,28 @@
                 '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')}")
+        try:
+            result = self.ffx.run(
+                'component destroy /core/session-manager/session:session',
+                skip_status_code_check=True)
+
+            if result.returncode == 0:
+                self.log.debug(f"Stopped session: {result.stdout}.")
+            else:
+                if (b'InstanceNotFound' in result.stderr
+                        or b'instance was not found' in result.stderr):
+                    self.log.debug(f'Instance was not found: {result.stderr}.')
+                else:
+                    raise WlanPolicyControllerError(
+                        f'Failed to stop the session: {result.stderr}.')
+        except FFXTimeout or FFXError as e:
+            raise WlanPolicyControllerError from e
 
         # Acquire control of policy layer
         controller_errors = []
-        while time.time() < end_time:
+        while time.time() < end_time_sec:
             # Create a client controller
-            response = self.device.wlan_policy_lib.wlanCreateClientController()
+            response = self.sl4f.wlan_policy_lib.wlanCreateClientController()
             if response.get('error'):
                 controller_errors.append(response['error'])
                 self.log.debug(response['error'])
@@ -96,7 +111,7 @@
                 continue
             # Attempt to use the client controller (failure indicates a closed
             # channel, meaning the client controller was rejected.
-            response = self.device.wlan_policy_lib.wlanGetSavedNetworks()
+            response = self.sl4f.wlan_policy_lib.wlanGetSavedNetworks()
             if response.get('error'):
                 controller_errors.append(response['error'])
                 self.log.debug(response['error'])
@@ -127,21 +142,14 @@
                 'Failed to stop client connections during deconfiguration.')
         self.policy_configured = False
 
-    def _clean_up(self):
+    def clean_up(self) -> None:
         if self.preserved_networks_and_client_state:
             # It is possible for policy to have been configured before, but
             # deconfigured before test end. In this case, in must be setup
             # before restoring networks
             if not self.policy_configured:
-                self._configure_wlan()
+                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
@@ -149,8 +157,7 @@
 
         Returns:
             True, if successful. False otherwise."""
-        start_response = self.device.wlan_policy_lib.wlanStartClientConnections(
-        )
+        start_response = self.sl4f.wlan_policy_lib.wlanStartClientConnections()
         if start_response.get('error'):
             self.log.error('Failed to start client connections. Err: %s' %
                            start_response['error'])
@@ -163,7 +170,7 @@
 
         Returns:
             True, if successful. False otherwise."""
-        stop_response = self.device.wlan_policy_lib.wlanStopClientConnections()
+        stop_response = self.sl4f.wlan_policy_lib.wlanStopClientConnections()
         if stop_response.get('error'):
             self.log.error('Failed to stop client connections. Err: %s' %
                            stop_response['error'])
@@ -178,7 +185,7 @@
 
         Args:
             ssid: string, the network name
-            security: string, security type of network (see wlan_policy_lib)
+            security: string, security type of network (see sl4f.wlan_policy_lib)
             password: string, the credential of the network if applicable
             timeout: int, time in seconds to wait for connection
 
@@ -189,7 +196,7 @@
         if not self.save_network(ssid, security, password=password):
             return False
         # Make connect call and check response
-        self.device.wlan_policy_lib.wlanSetNewListener()
+        self.sl4f.wlan_policy_lib.wlanSetNewListener()
         if not self.send_connect_command(ssid, security):
             return False
         return self.wait_for_connect(ssid, security, timeout=timeout)
@@ -208,7 +215,7 @@
 
         Args:
             ssid: string, the network name
-            security: string, security type of network (see wlan_policy_lib)
+            security: string, security type of network (see sl4f.wlan_policy_lib)
             password: string, the credential of the network if applicable
             timeout: int, time in seconds to wait for connection
 
@@ -232,7 +239,7 @@
 
         Args:
             ssid: string, the network name
-            security: string, security type of network (see wlan_policy_lib)
+            security: string, security type of network (see sl4f.wlan_policy_lib)
             password: string, the credential of the network if applicable
             state: string, The connection state we are expecting, ie "Disconnected" or
                 "Failed"
@@ -243,7 +250,7 @@
         Returns:
             True, if successful. False otherwise.
         """
-        self.device.wlan_policy_lib.wlanSetNewListener()
+        self.sl4f.wlan_policy_lib.wlanSetNewListener()
         if not self.remove_network(ssid, security_type, password=password):
             return False
         return self.wait_for_disconnect(ssid,
@@ -259,7 +266,7 @@
         Returns:
             True, if successful. False otherwise.
         """
-        self.device.wlan_policy_lib.wlanSetNewListener()
+        self.sl4f.wlan_policy_lib.wlanSetNewListener()
         if not self.remove_all_networks():
             self.log.error('Failed to remove all networks. Cannot continue to '
                            'wait_for_no_connections.')
@@ -271,13 +278,13 @@
 
         Args:
             ssid: string, the network name
-            security: string, security type of network (see wlan_policy_lib)
+            security: string, security type of network (see sl4f.wlan_policy_lib)
             password: string, the credential of the network if applicable
 
         Returns:
             True, if successful. False otherwise.
         """
-        save_response = self.device.wlan_policy_lib.wlanSaveNetwork(
+        save_response = self.sl4f.wlan_policy_lib.wlanSaveNetwork(
             ssid, security_type, target_pwd=password)
         if save_response.get('error'):
             self.log.error('Failed to save network %s with error: %s' %
@@ -290,13 +297,13 @@
 
         Args:
             ssid: string, the network name
-            security: string, security type of network (see wlan_policy_lib)
+            security: string, security type of network (see sl4f.wlan_policy_lib)
             password: string, the credential of the network if applicable
 
         Returns:
             True, if successful. False otherwise.
         """
-        remove_response = self.device.wlan_policy_lib.wlanRemoveNetwork(
+        remove_response = self.sl4f.wlan_policy_lib.wlanRemoveNetwork(
             ssid, security_type, target_pwd=password)
         if remove_response.get('error'):
             self.log.error('Failed to remove network %s with error: %s' %
@@ -310,8 +317,7 @@
         Returns:
             True, if successful. False otherwise.
         """
-        remove_all_response = self.device.wlan_policy_lib.wlanRemoveAllNetworks(
-        )
+        remove_all_response = self.sl4f.wlan_policy_lib.wlanRemoveAllNetworks()
         if remove_all_response.get('error'):
             self.log.error('Error occurred removing all networks: %s' %
                            remove_all_response['error'])
@@ -327,7 +333,7 @@
         Raises:
             WlanPolicyControllerError, if retrieval fails.
         """
-        saved_networks_response = self.device.wlan_policy_lib.wlanGetSavedNetworks(
+        saved_networks_response = self.sl4f.wlan_policy_lib.wlanGetSavedNetworks(
         )
         if saved_networks_response.get('error'):
             raise WlanPolicyControllerError(
@@ -342,13 +348,13 @@
 
         Args:
             ssid: string, the network name
-            security: string, security type of network (see wlan_policy_lib)
+            security: string, security type of network (see sl4f.wlan_policy_lib)
             password: string, the credential of the network if applicable
 
         Returns:
             True, if command send successfully. False otherwise.
         """
-        connect_response = self.device.wlan_policy_lib.wlanConnect(
+        connect_response = self.sl4f.wlan_policy_lib.wlanConnect(
             ssid, security_type)
         if connect_response.get('error'):
             self.log.error(
@@ -361,7 +367,7 @@
         """ Wait until the device has connected to the specified network.
         Args:
             ssid: string, the network name
-            security: string, security type of network (see wlan_policy_lib)
+            security: string, security type of network (see sl4f.wlan_policy_lib)
             timeout: int, seconds to wait for a update showing connection
         Returns:
             True if we see a connect to the network, False otherwise.
@@ -373,15 +379,15 @@
             time_left = max(1, int(end_time - time.time()))
 
             try:
-                update = self.device.wlan_policy_lib.wlanGetUpdate(
+                update = self.sl4f.wlan_policy_lib.wlanGetUpdate(
                     timeout=time_left)
-            except requests.exceptions.Timeout:
+            except TimeoutError:
                 self.log.error('Timed out waiting for response from device '
                                'while waiting for network with SSID "%s" to '
                                'connect. Device took too long to connect or '
                                'the request timed out for another reason.' %
                                ssid)
-                self.device.wlan_policy_lib.wlanSetNewListener()
+                self.sl4f.wlan_policy_lib.wlanSetNewListener()
                 return False
             if update.get('error'):
                 # This can occur for many reasons, so it is not necessarily a
@@ -418,7 +424,7 @@
 
         Args:
             ssid: string, the network name
-            security: string, security type of network (see wlan_policy_lib)
+            security: string, security type of network (see sl4f.wlan_policy_lib)
             state: string, The connection state we are expecting, ie "Disconnected" or
                 "Failed"
             status: string, The disconnect status we expect, it "ConnectionStopped" or
@@ -436,15 +442,15 @@
         while time.time() < end_time:
             time_left = max(1, int(end_time - time.time()))
             try:
-                update = self.device.wlan_policy_lib.wlanGetUpdate(
+                update = self.sl4f.wlan_policy_lib.wlanGetUpdate(
                     timeout=time_left)
-            except requests.exceptions.Timeout:
+            except TimeoutError:
                 self.log.error(
                     'Timed out waiting for response from device '
                     'while waiting for network with SSID "%s" to '
                     'disconnect. Device took too long to disconnect '
                     'or the request timed out for another reason.' % ssid)
-                self.device.wlan_policy_lib.wlanSetNewListener()
+                self.sl4f.wlan_policy_lib.wlanSetNewListener()
                 return False
 
             if update.get('error'):
@@ -506,18 +512,18 @@
         # If there are already no existing connections when this function is called,
         # then an update won't be generated by the device, and we'll time out.
         # Force an update by getting a new listener.
-        self.device.wlan_policy_lib.wlanSetNewListener()
+        self.sl4f.wlan_policy_lib.wlanSetNewListener()
         end_time = time.time() + timeout
         while time.time() < end_time:
             time_left = max(1, int(end_time - time.time()))
             try:
-                update = self.device.wlan_policy_lib.wlanGetUpdate(
+                update = self.sl4f.wlan_policy_lib.wlanGetUpdate(
                     timeout=time_left)
-            except requests.exceptions.Timeout:
+            except TimeoutError:
                 self.log.info(
                     "Timed out getting status update while waiting for all"
                     " connections to end.")
-                self.device.wlan_policy_lib.wlanSetNewListener()
+                self.sl4f.wlan_policy_lib.wlanSetNewListener()
                 return False
 
             if update["error"] != None:
@@ -544,7 +550,7 @@
         """
         # Save preexisting saved networks
         preserved_networks_and_state = {}
-        saved_networks_response = self.device.wlan_policy_lib.wlanGetSavedNetworks(
+        saved_networks_response = self.sl4f.wlan_policy_lib.wlanGetSavedNetworks(
         )
         if saved_networks_response.get('error'):
             raise WlanPolicyControllerError(
@@ -560,8 +566,8 @@
                 'Failed to clear networks and disconnect at FuchsiaDevice creation.'
             )
 
-        self.device.wlan_policy_lib.wlanSetNewListener()
-        update_response = self.device.wlan_policy_lib.wlanGetUpdate()
+        self.sl4f.wlan_policy_lib.wlanSetNewListener()
+        update_response = self.sl4f.wlan_policy_lib.wlanGetUpdate()
         update_result = update_response.get('result', {})
         if update_result.get('state'):
             preserved_networks_and_state[CLIENT_STATE] = update_result['state']
diff --git a/acts/framework/acts/controllers/fuchsia_lib/light_lib.py b/acts/framework/acts/controllers/fuchsia_lib/light_lib.py
deleted file mode 100644
index d0ad168..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/light_lib.py
+++ /dev/null
@@ -1,342 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright 2020 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-from acts.controllers.fuchsia_lib.base_lib import BaseLib
-
-
-class RgbValue:
-    # use __dict__ to send in json
-    def __init__(self, r, g, b):
-        self.red = r
-        self.green = g
-        self.blue = b
-
-
-class FuchsiaLightLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-
-    def getNumLights(self):
-        """ Gets the total number of physical lights on the Fuchsia device
-        under test.
-
-        Returns:
-            Integer (number of lights), prints an error message if error.
-        """
-        test_cmd = "light_facade.GetNumLights"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getNumLightGroups(self):
-        """ Gets the total number of light groups on the Fuchsia device
-        under test.
-
-        Returns:
-            Integer (number of light groups), prints an error message if error.
-        """
-        test_cmd = "light_facade.GetNumLightGroups"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getInfo(self, index):
-        """ Gets info (name and capability) for a single light on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light defined by board, must be less than
-                   value returned by getNumLights
-
-        Returns:
-            The Info object, prints an error message if error.
-        """
-        test_cmd = "light_facade.GetInfo"
-        test_args = {"index": index}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getCurrentSimpleValue(self, index):
-        """ Gets the current simple value for a single light on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light defined by board, must be less than
-                   value returned by getNumLights
-
-        Returns:
-            Boolean. True if the light is ON, False if the light is OFF.
-            If the capability 'SIMPLE' is not supported by this light, fails and prints
-            error message.
-            Use GetInfo to check if light supports this operation.
-        """
-        test_cmd = "light_facade.GetCurrentSimpleValue"
-        test_args = {"index": index}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def setSimpleValue(self, index, value):
-        """ Sets the simple value for a single light on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light defined by board, must be less than
-                   value returned by getNumLights
-            value: boolean, True for ON, False for OFF
-
-        Returns:
-            None if success, prints error message if error (e.g. capability 'SIMPLE'
-            is not supported by the light).
-        """
-        test_cmd = "light_facade.SetSimpleValue"
-        test_args = {"index": index, "value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getCurrentBrightnessValue(self, index):
-        """ Gets the current brightness value for a single light on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light defined by board, must be less than
-                   value returned by getNumLights
-
-        Returns:
-            uint8. A value 0-255 inclusive indicating the brightness of the light.
-            If the capability 'BRIGHTNESS' is not supported by this light, fails and prints
-            error message.
-            Use GetInfo to check if light supports this operation.
-        """
-        test_cmd = "light_facade.GetCurrentBrightnessValue"
-        test_args = {"index": index}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def setBrightnessValue(self, index, value):
-        """ Sets the brightness value for a single light on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light defined by board, must be less than
-                   value returned by getNumLights
-            value: uint8, 0-255 inclusive indicating the brightness of the light
-
-        Returns:
-            None if success, prints error message if error (e.g. capability
-            'BRIGHTNESS' is not supported by the light).
-        """
-        test_cmd = "light_facade.SetBrightnessValue"
-        test_args = {"index": index, "value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getCurrentRgbValue(self, index):
-        """ Gets the current RGB value for a single light on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light defined by board, must be less than
-                   value returned by getNumLights
-
-        Returns:
-            The RGB object indicating the RGB value of the light.
-            If the capability 'RGB' is not supported by this light, fails and prints
-            error message.
-            Use GetInfo to check if light supports this operation.
-        """
-        test_cmd = "light_facade.GetCurrentRgbValue"
-        test_args = {"index": index}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def setRgbValue(self, index, value):
-        """ Sets the RGB value for a single light on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light defined by board, must be less than
-                   value returned by getNumLights
-            value: an RGB object
-
-        Returns:
-            None if success, prints error message if error (e.g. capability 'RGB'
-            is not supported by the light).
-        """
-        test_cmd = "light_facade.SetRgbValue"
-        test_args = {"index": index, "value": value}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getGroupInfo(self, index):
-        """ Gets group info (name, number of lights, and capability) for a light
-        group on the Fuchsia device under test.
-
-        Args:
-            index: uint32, index of the light group defined by board, must be less
-                   than value returned by getNumLightGroups
-
-        Returns:
-            The GroupInfo object, prints an error message if error.
-        """
-        test_cmd = "light_facade.GetGroupInfo"
-        test_args = {"index": index}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getGroupCurrentSimpleValue(self, index):
-        """ Gets the current simple values for the light group on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light group defined by board, must be less than
-                   value returned by getNumLightGroups
-
-        Returns:
-            Vector of booleans, each indicating whether or not the light is on.
-            If the capability 'SIMPLE' is not supported by the light group, fails and prints
-            error message.
-            Use GetGroupInfo to check if light supports this operation.
-        """
-        test_cmd = "light_facade.GetGroupCurrentSimpleValue"
-        test_args = {"index": index}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def setGroupSimpleValue(self, index, values):
-        """ Sets the simple value for a light group on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light defined by board, must be less than
-                   value returned by getNumLights
-            values: a vector of booleans, each has True for ON, False for OFF
-
-        Returns:
-            None if success, prints error message if error (e.g. capability 'SIMPLE'
-            is not supported by the light group).
-        """
-        test_cmd = "light_facade.SetGroupSimpleValue"
-        test_args = {"index": index, "values": values}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getGroupCurrentBrightnessValue(self, index):
-        """ Gets the current brightness values for the light group on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light group defined by board, must be less than
-                   value returned by getNumLightGroups
-
-        Returns:
-            Vector of uint8, each indicating the brightness of the light.
-            If the capability 'SIMPLE' is not supported by the light group, fails and prints
-            error message.
-            Use GetGroupInfo to check if light supports this operation.
-        """
-        test_cmd = "light_facade.GetGroupCurrentBrightnessValue"
-        test_args = {"index": index}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def setGroupBrightnessValue(self, index, values):
-        """ Sets the brightness value for a light group on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light defined by board, must be less than
-                   value returned by getNumLights
-            values: a vector of uint8s, each indicating the brightness of a light.
-
-        Returns:
-            None if success, prints error message if error (e.g. capability 'BRIGHTNESS'
-            is not supported by the light group).
-        """
-        test_cmd = "light_facade.SetGroupBrightnessValue"
-        test_args = {"index": index, "values": values}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getGroupCurrentRgbValue(self, index):
-        """ Gets the current RGB values for the light group on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light group defined by board, must be less than
-                   value returned by getNumLightGroups
-
-        Returns:
-            Vector of RGB objects, each indicating the RGB value of the light.
-            If the capability 'RGB' is not supported by the light group, fails and prints
-            error message.
-            Use GetGroupInfo to check if light supports this operation.
-        """
-        test_cmd = "light_facade.GetGroupCurrentRgbValue"
-        test_args = {"index": index}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def setGroupRgbValue(self, index, values):
-        """ Sets the RGB value for a light group on the Fuchsia device
-        under test.
-
-        Args:
-            index: uint32, index of the light defined by board, must be less than
-                   value returned by getNumLights
-            values: a vector of RGB objects, each indicating the RGB value of a light.
-
-        Returns:
-            None if success, prints error message if error (e.g. capability 'RGB'
-            is not supported by the light group).
-        """
-        test_cmd = "light_facade.SetGroupRgbValue"
-        test_args = {"index": index, "values": values}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/location/regulatory_region_lib.py b/acts/framework/acts/controllers/fuchsia_lib/location/regulatory_region_lib.py
index b755db8..8519785 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/location/regulatory_region_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/location/regulatory_region_lib.py
@@ -18,10 +18,9 @@
 
 
 class FuchsiaRegulatoryRegionLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "location_regulatory_region")
 
     # TODO(fxb/46727): Provide an analagous call to check the region
     # configured into the driver.
@@ -36,7 +35,5 @@
         """
         test_cmd = "location_regulatory_region_facade.set_region"
         test_args = {"region": region_code}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/logging_lib.py b/acts/framework/acts/controllers/fuchsia_lib/logging_lib.py
index 78a9dd4..25826e3 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/logging_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/logging_lib.py
@@ -20,10 +20,9 @@
 
 
 class FuchsiaLoggingLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "logging")
 
     def logE(self, message):
         """Log a message of level Error directly to the syslog.
@@ -38,10 +37,8 @@
         test_args = {
             "message": '[%s] %s' % (datetime.datetime.now(), message),
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def logI(self, message):
         """Log a message of level Info directly to the syslog.
@@ -54,10 +51,8 @@
         """
         test_cmd = "logging_facade.LogInfo"
         test_args = {"message": '[%s] %s' % (datetime.datetime.now(), message)}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def logW(self, message):
         """Log a message of level Warning directly to the syslog.
@@ -70,7 +65,5 @@
         """
         test_cmd = "logging_facade.LogWarn"
         test_args = {"message": '[%s] %s' % (datetime.datetime.now(), message)}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/netstack/netstack_lib.py b/acts/framework/acts/controllers/fuchsia_lib/netstack/netstack_lib.py
index 173127c..9344f7f 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/netstack/netstack_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/netstack/netstack_lib.py
@@ -18,10 +18,9 @@
 
 
 class FuchsiaNetstackLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "netstack")
 
     def netstackListInterfaces(self):
         """ListInterfaces command
@@ -31,10 +30,8 @@
         """
         test_cmd = "netstack_facade.ListInterfaces"
         test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def enableInterface(self, id):
         """Enable Interface
@@ -47,10 +44,8 @@
         """
         test_cmd = "netstack_facade.EnableInterface"
         test_args = {"identifier": id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def disableInterface(self, id):
         """Disable Interface
@@ -63,7 +58,5 @@
         """
         test_cmd = "netstack_facade.DisableInterface"
         test_args = {"identifier": id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/package_server.py b/acts/framework/acts/controllers/fuchsia_lib/package_server.py
new file mode 100644
index 0000000..1bc0448
--- /dev/null
+++ b/acts/framework/acts/controllers/fuchsia_lib/package_server.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import json
+import os
+import shutil
+import socket
+import subprocess
+import tarfile
+import tempfile
+
+from dataclasses import dataclass
+from datetime import datetime
+from typing import TextIO, List, Optional
+
+from acts import context
+from acts import logger
+from acts import signals
+from acts import utils
+
+from acts.controllers.fuchsia_lib.ssh import FuchsiaSSHError, SSHProvider
+from acts.controllers.fuchsia_lib.utils_lib import wait_for_port
+from acts.tracelogger import TraceLogger
+
+DEFAULT_FUCHSIA_REPO_NAME = "fuchsia.com"
+PM_SERVE_STOP_TIMEOUT_SEC = 5
+
+
+class PackageServerError(signals.TestAbortClass):
+    pass
+
+
+def random_port() -> int:
+    s = socket.socket()
+    s.bind(('', 0))
+    return s.getsockname()[1]
+
+
+@dataclass
+class Route:
+    """Represent a route in the routing table."""
+    preferred_source: Optional[str]
+
+
+def find_routes_to(dest_ip) -> List[Route]:
+    """Find the routes used to reach a destination.
+
+    Look through the routing table for the routes that would be used without
+    sending any packets. This is especially helpful for when the device is
+    currently unreachable.
+
+    Only natively supported on Linux. MacOS has iproute2mac, but it doesn't
+    support JSON formatted output.
+
+    TODO(http://b/238924195): Add support for MacOS.
+
+    Args:
+        dest_ip: IP address of the destination
+
+    Throws:
+        CalledProcessError: if the ip command returns a non-zero exit code
+        JSONDecodeError: if the ip command doesn't return JSON
+
+    Returns:
+        Routes with destination to dest_ip.
+    """
+    resp = subprocess.run(f"ip -json route get {dest_ip}".split(),
+                          capture_output=True,
+                          check=True)
+    routes = json.loads(resp.stdout)
+    return [Route(r.get("prefsrc")) for r in routes]
+
+
+def find_host_ip(device_ip: str) -> str:
+    """Find the host's source IP used to reach a device.
+
+    Not all host interfaces can talk to a given device. This limitation can
+    either be physical through hardware or virtual through routing tables.
+    Look through the routing table without sending any packets then return the
+    preferred source IP address.
+
+    Args:
+        device_ip: IP address of the device
+
+    Raises:
+        PackageServerError: if there are multiple or no routes to device_ip, or
+            if the route doesn't contain "prefsrc"
+
+    Returns:
+        The host IP used to reach device_ip.
+    """
+    routes = find_routes_to(device_ip)
+    if len(routes) != 1:
+        raise PackageServerError(
+            f"Expected only one route to {device_ip}, got {routes}")
+
+    route = routes[0]
+    if not route.preferred_source:
+        raise PackageServerError(f'Route does not contain "prefsrc": {route}')
+    return route.preferred_source
+
+
+class PackageServer:
+    """Package manager for Fuchsia; an interface to the "pm" CLI tool."""
+
+    def __init__(self, packages_archive_path: str) -> None:
+        """
+        Args:
+            packages_archive_path: Path to an archive containing the pm binary
+                and amber-files.
+        """
+        self.log: TraceLogger = logger.create_tagged_trace_logger("pm")
+
+        self._server_log: Optional[TextIO] = None
+        self._server_proc: Optional[subprocess.Popen] = None
+        self._log_path: Optional[str] = None
+
+        self._tmp_dir = tempfile.mkdtemp(prefix="packages-")
+        tar = tarfile.open(packages_archive_path, "r:gz")
+        tar.extractall(self._tmp_dir)
+
+        self._binary_path = os.path.join(self._tmp_dir, "pm")
+        self._packages_path = os.path.join(self._tmp_dir, "amber-files")
+        self._port = random_port()
+
+        self._assert_repo_has_not_expired()
+
+    def clean_up(self) -> None:
+        if self._server_proc:
+            self.stop_server()
+        if self._tmp_dir:
+            shutil.rmtree(self._tmp_dir)
+
+    def _assert_repo_has_not_expired(self) -> None:
+        """Abort if the repository metadata has expired.
+
+        Raises:
+            TestAbortClass: when the timestamp.json file has expired
+        """
+        with open(f'{self._packages_path}/repository/timestamp.json',
+                  'r') as f:
+            data = json.load(f)
+            expiresAtRaw = data["signed"]["expires"]
+            expiresAt = datetime.strptime(expiresAtRaw, '%Y-%m-%dT%H:%M:%SZ')
+            if expiresAt <= datetime.now():
+                raise signals.TestAbortClass(
+                    f'{self._packages_path}/repository/timestamp.json has expired on {expiresAtRaw}'
+                )
+
+    def start(self) -> None:
+        """Start the package server.
+
+        Does not check for errors; view the log file for any errors.
+        """
+        if self._server_proc:
+            self.log.warn(
+                "Skipping to start the server since it has already been started"
+            )
+            return
+
+        pm_command = f'{self._binary_path} serve -c 2 -repo {self._packages_path} -l :{self._port}'
+
+        root_dir = context.get_current_context().get_full_output_path()
+        epoch = utils.get_current_epoch_time()
+        time_stamp = logger.normalize_log_line_timestamp(
+            logger.epoch_to_log_line_timestamp(epoch))
+        self._log_path = os.path.join(root_dir, f'pm_server.{time_stamp}.log')
+
+        self._server_log = open(self._log_path, 'a+')
+        self._server_proc = subprocess.Popen(pm_command.split(),
+                                             preexec_fn=os.setpgrp,
+                                             stdout=self._server_log,
+                                             stderr=subprocess.STDOUT)
+        try:
+            wait_for_port('127.0.0.1', self._port)
+        except TimeoutError as e:
+            if self._server_log:
+                self._server_log.close()
+            if self._log_path:
+                with open(self._log_path, 'r') as f:
+                    logs = f.read()
+            raise TimeoutError(
+                f"pm serve failed to expose port {self._port}. Logs:\n{logs}"
+            ) from e
+
+        self.log.info(f'Serving packages on port {self._port}')
+
+    def configure_device(self,
+                         ssh: SSHProvider,
+                         repo_name=DEFAULT_FUCHSIA_REPO_NAME) -> None:
+        """Configure the device to use this package server.
+
+        Args:
+            ssh: Device SSH transport channel
+            repo_name: Name of the repo to alias this package server
+        """
+        # Remove any existing repositories that may be stale.
+        try:
+            ssh.run(f'pkgctl repo rm fuchsia-pkg://{repo_name}')
+        except FuchsiaSSHError as e:
+            if 'NOT_FOUND' not in e.result.stderr:
+                raise e
+
+        # Configure the device with the new repository.
+        host_ip = find_host_ip(ssh.config.host_name)
+        repo_url = f"http://{host_ip}:{self._port}"
+        ssh.run(
+            f"pkgctl repo add url -f 2 -n {repo_name} {repo_url}/config.json")
+        self.log.info(
+            f'Added repo "{repo_name}" as {repo_url} on device {ssh.config.host_name}'
+        )
+
+    def stop_server(self) -> None:
+        """Stop the package server."""
+        if not self._server_proc:
+            self.log.warn(
+                "Skipping to stop the server since it hasn't been started yet")
+            return
+
+        self._server_proc.terminate()
+        try:
+            self._server_proc.wait(timeout=PM_SERVE_STOP_TIMEOUT_SEC)
+        except subprocess.TimeoutExpired:
+            self.log.warn(
+                f"Taking over {PM_SERVE_STOP_TIMEOUT_SEC}s to stop. Killing the server"
+            )
+            self._server_proc.kill()
+            self._server_proc.wait(timeout=PM_SERVE_STOP_TIMEOUT_SEC)
+        finally:
+            if self._server_log:
+                self._server_log.close()
+
+        self._server_proc = None
+        self._log_path = None
+        self._server_log = None
diff --git a/acts/framework/acts/controllers/fuchsia_lib/ram_lib.py b/acts/framework/acts/controllers/fuchsia_lib/ram_lib.py
deleted file mode 100644
index ce8bb73..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/ram_lib.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright 2020 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-from acts.controllers.fuchsia_lib.base_lib import BaseLib
-
-
-class FuchsiaRamLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-
-    def measureBandwidth(self, cycles_to_measure, channels):
-        """ Measures the DDR bandwidth on the specified channels.
-
-        Args:
-            cycles_to_measure: How many bus cycles to perform the measurement over.
-            channels: An array of 8 uint64, specifying which ports to aggregate
-                      for each channel.
-
-        Returns:
-            BandwidthInfo struct, prints an error message if error.
-        """
-        test_cmd = "ram_facade.MeasureBandwidth"
-        test_args = {
-            "values": {
-                "cycles_to_measure": cycles_to_measure,
-                "channels": channels
-            }
-        }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getDdrWindowingResults(self):
-        """ Retrieves the results from the DDR Windowing tool, which runs in
-            the bootloader.
-
-        Returns:
-            The register value, prints an error message if error.
-        """
-        test_cmd = "ram_facade.GetDdrWindowingResults"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/session_manager_lib.py b/acts/framework/acts/controllers/fuchsia_lib/session_manager_lib.py
deleted file mode 100644
index fc03c14..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/session_manager_lib.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/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.run(
-                "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.run(
-            "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/sl4f.py b/acts/framework/acts/controllers/fuchsia_lib/sl4f.py
new file mode 100644
index 0000000..69bf2f5
--- /dev/null
+++ b/acts/framework/acts/controllers/fuchsia_lib/sl4f.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import ipaddress
+
+from acts import logger
+from acts.controllers.fuchsia_lib import utils_lib
+from acts.controllers.fuchsia_lib.audio_lib import FuchsiaAudioLib
+from acts.controllers.fuchsia_lib.basemgr_lib import FuchsiaBasemgrLib
+from acts.controllers.fuchsia_lib.bt.avdtp_lib import FuchsiaAvdtpLib
+from acts.controllers.fuchsia_lib.bt.ble_lib import FuchsiaBleLib
+from acts.controllers.fuchsia_lib.bt.bts_lib import FuchsiaBtsLib
+from acts.controllers.fuchsia_lib.bt.gattc_lib import FuchsiaGattcLib
+from acts.controllers.fuchsia_lib.bt.gatts_lib import FuchsiaGattsLib
+from acts.controllers.fuchsia_lib.bt.hfp_lib import FuchsiaHfpLib
+from acts.controllers.fuchsia_lib.bt.rfcomm_lib import FuchsiaRfcommLib
+from acts.controllers.fuchsia_lib.bt.sdp_lib import FuchsiaProfileServerLib
+from acts.controllers.fuchsia_lib.hardware_power_statecontrol_lib import FuchsiaHardwarePowerStatecontrolLib
+from acts.controllers.fuchsia_lib.location.regulatory_region_lib import FuchsiaRegulatoryRegionLib
+from acts.controllers.fuchsia_lib.logging_lib import FuchsiaLoggingLib
+from acts.controllers.fuchsia_lib.netstack.netstack_lib import FuchsiaNetstackLib
+from acts.controllers.fuchsia_lib.ssh import SSHProvider, FuchsiaSSHError
+from acts.controllers.fuchsia_lib.wlan_ap_policy_lib import FuchsiaWlanApPolicyLib
+from acts.controllers.fuchsia_lib.wlan_deprecated_configuration_lib import FuchsiaWlanDeprecatedConfigurationLib
+from acts.controllers.fuchsia_lib.wlan_lib import FuchsiaWlanLib
+from acts.controllers.fuchsia_lib.wlan_policy_lib import FuchsiaWlanPolicyLib
+
+DEFAULT_SL4F_PORT = 80
+START_SL4F_V2_CMD = 'start_sl4f'
+
+
+class SL4F:
+    """Module for Fuchsia devices to interact with the SL4F tool.
+
+    Attributes:
+        ssh: SSHProvider transport to start and stop SL4F.
+        address: http address for SL4F server including SL4F port.
+        log: Logger for the device-specific instance of SL4F.
+    """
+
+    def __init__(self, ssh: SSHProvider,
+                 port: int = DEFAULT_SL4F_PORT) -> None:
+        """
+        Args:
+            ssh: SSHProvider transport to start and stop SL4F.
+            port: Port for the SL4F server to listen on.
+        """
+        host = ipaddress.ip_address(ssh.config.host_name)
+        if host.version == 4:
+            self.address = f'http://{host}:{port}'
+        elif host.version == 6:
+            self.address = f'http://[{host}]:{port}'
+
+        self.log = logger.create_tagged_trace_logger(f"SL4F | {self.address}")
+
+        try:
+            ssh.run(START_SL4F_V2_CMD).stdout
+        except FuchsiaSSHError:
+            # TODO(fxbug.dev/99331) Remove support to run SL4F in CFv1 mode
+            # once ACTS no longer use images that comes with only CFv1 SL4F.
+            self.log.warn(
+                "Running SL4F in CFv1 mode, "
+                "this is deprecated for images built after 5/9/2022, "
+                "see https://fxbug.dev/77056 for more info.")
+            ssh.stop_v1_component("sl4f")
+            ssh.start_v1_component("sl4f")
+
+        utils_lib.wait_for_port(str(host), port)
+        self._init_libraries()
+        self._verify_sl4f_connection()
+
+    def _init_libraries(self) -> None:
+        # Grab commands from FuchsiaAudioLib
+        self.audio_lib = FuchsiaAudioLib(self.address)
+
+        # Grab commands from FuchsiaAvdtpLib
+        self.avdtp_lib = FuchsiaAvdtpLib(self.address)
+
+        # Grab commands from FuchsiaHfpLib
+        self.hfp_lib = FuchsiaHfpLib(self.address)
+
+        # Grab commands from FuchsiaRfcommLib
+        self.rfcomm_lib = FuchsiaRfcommLib(self.address)
+
+        # Grab commands from FuchsiaBasemgrLib
+        self.basemgr_lib = FuchsiaBasemgrLib(self.address)
+
+        # Grab commands from FuchsiaBleLib
+        self.ble_lib = FuchsiaBleLib(self.address)
+
+        # Grab commands from FuchsiaBtsLib
+        self.bts_lib = FuchsiaBtsLib(self.address)
+
+        # Grab commands from FuchsiaGattcLib
+        self.gattc_lib = FuchsiaGattcLib(self.address)
+
+        # Grab commands from FuchsiaGattsLib
+        self.gatts_lib = FuchsiaGattsLib(self.address)
+
+        # Grab commands from FuchsiaHardwarePowerStatecontrolLib
+        self.hardware_power_statecontrol_lib = (
+            FuchsiaHardwarePowerStatecontrolLib(self.address))
+
+        # Grab commands from FuchsiaLoggingLib
+        self.logging_lib = FuchsiaLoggingLib(self.address)
+
+        # Grab commands from FuchsiaNetstackLib
+        self.netstack_lib = FuchsiaNetstackLib(self.address)
+
+        # Grab commands from FuchsiaProfileServerLib
+        self.sdp_lib = FuchsiaProfileServerLib(self.address)
+
+        # Grab commands from FuchsiaRegulatoryRegionLib
+        self.regulatory_region_lib = FuchsiaRegulatoryRegionLib(self.address)
+
+        # Grabs command from FuchsiaWlanDeprecatedConfigurationLib
+        self.wlan_deprecated_configuration_lib = (
+            FuchsiaWlanDeprecatedConfigurationLib(self.address))
+
+        # Grab commands from FuchsiaWlanLib
+        self.wlan_lib = FuchsiaWlanLib(self.address)
+
+        # Grab commands from FuchsiaWlanApPolicyLib
+        self.wlan_ap_policy_lib = FuchsiaWlanApPolicyLib(self.address)
+
+        # Grab commands from FuchsiaWlanPolicyLib
+        self.wlan_policy_lib = FuchsiaWlanPolicyLib(self.address)
+
+    def _verify_sl4f_connection(self) -> None:
+        """Verify SL4F commands can run on server."""
+
+        self.log.info('Verifying SL4F commands can run.')
+        try:
+            self.wlan_lib.wlanGetIfaceIdList()
+        except Exception as err:
+            raise ConnectionError(
+                f'Failed to connect and run command via SL4F. Err: {err}')
diff --git a/acts/framework/acts/controllers/fuchsia_lib/ssh.py b/acts/framework/acts/controllers/fuchsia_lib/ssh.py
new file mode 100644
index 0000000..59ebfc9
--- /dev/null
+++ b/acts/framework/acts/controllers/fuchsia_lib/ssh.py
@@ -0,0 +1,270 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0SSHResults
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 subprocess
+import time
+
+from dataclasses import dataclass
+from typing import List, Union
+
+from acts import logger
+from acts import signals
+
+DEFAULT_SSH_USER: str = "fuchsia"
+DEFAULT_SSH_PORT: int = 22
+DEFAULT_SSH_TIMEOUT_SEC: int = 60
+DEFAULT_SSH_CONNECT_TIMEOUT_SEC: int = 30
+DEFAULT_SSH_SERVER_ALIVE_INTERVAL: int = 30
+# The default package repository for all components.
+FUCHSIA_PACKAGE_REPO_NAME = 'fuchsia.com'
+
+
+class SSHResult:
+    """Result of an SSH command."""
+
+    def __init__(
+        self, process: Union[subprocess.CompletedProcess,
+                             subprocess.CalledProcessError]
+    ) -> None:
+        self._raw_stdout = process.stdout
+        self._stdout = process.stdout.decode('utf-8', errors='replace')
+        self._stderr = process.stderr.decode('utf-8', errors='replace')
+        self._exit_status: int = process.returncode
+
+    def __str__(self):
+        if self.exit_status == 0:
+            return self.stdout
+        return f'status {self.exit_status}, stdout: "{self.stdout}", stderr: "{self.stderr}"'
+
+    @property
+    def stdout(self) -> str:
+        return self._stdout
+
+    @property
+    def stderr(self) -> str:
+        return self._stderr
+
+    @property
+    def exit_status(self) -> int:
+        return self._exit_status
+
+    @property
+    def raw_stdout(self) -> bytes:
+        return self._raw_stdout
+
+
+class FuchsiaSSHError(signals.TestError):
+    """A SSH command returned with a non-zero status code."""
+
+    def __init__(self, command: str, result: SSHResult):
+        super().__init__(
+            f'SSH command "{command}" unexpectedly returned {result}')
+        self.result = result
+
+
+class SSHTimeout(signals.TestError):
+    """A SSH command timed out."""
+
+    def __init__(self, err: subprocess.TimeoutExpired):
+        super().__init__(
+            f'SSH command "{err.cmd}" timed out after {err.timeout}s, '
+            f'stdout="{err.stdout}", stderr="{err.stderr}"')
+
+
+class FuchsiaSSHTransportError(signals.TestError):
+    """Failure to send an SSH command."""
+
+
+@dataclass
+class SSHConfig:
+    """SSH client config."""
+
+    # SSH flags. See ssh(1) for full details.
+    host_name: str
+    identity_file: str
+
+    ssh_binary: str = 'ssh'
+    config_file: str = '/dev/null'
+    port: int = 22
+    user: str = DEFAULT_SSH_USER
+
+    # SSH options. See ssh_config(5) for full details.
+    connect_timeout: int = DEFAULT_SSH_CONNECT_TIMEOUT_SEC
+    server_alive_interval: int = DEFAULT_SSH_SERVER_ALIVE_INTERVAL
+    strict_host_key_checking: bool = False
+    user_known_hosts_file: str = "/dev/null"
+    log_level: str = "ERROR"
+
+    def full_command(self, command: str, force_tty: bool = False) -> List[str]:
+        """Generate the complete command to execute command over SSH.
+
+        Args:
+            command: The command to run over SSH
+            force_tty: Force pseudo-terminal allocation. This can be used to
+                execute arbitrary screen-based programs on a remote machine,
+                which can be very useful, e.g. when implementing menu services.
+
+        Returns:
+            Arguments composing the complete call to SSH.
+        """
+        optional_flags = []
+        if force_tty:
+            # Multiple -t options force tty allocation, even if ssh has no local
+            # tty. This is necessary for launching ssh with subprocess without
+            # shell=True.
+            optional_flags.append('-tt')
+
+        return [
+            self.ssh_binary,
+            # SSH flags
+            '-i',
+            self.identity_file,
+            '-F',
+            self.config_file,
+            '-p',
+            str(self.port),
+            # SSH configuration options
+            '-o',
+            f'ConnectTimeout={self.connect_timeout}',
+            '-o',
+            f'ServerAliveInterval={self.server_alive_interval}',
+            '-o',
+            f'StrictHostKeyChecking={"yes" if self.strict_host_key_checking else "no"}',
+            '-o',
+            f'UserKnownHostsFile={self.user_known_hosts_file}',
+            '-o',
+            f'LogLevel={self.log_level}',
+        ] + optional_flags + [
+            f'{self.user}@{self.host_name}'
+        ] + command.split()
+
+
+class SSHProvider:
+    """Device-specific provider for SSH clients."""
+
+    def __init__(self, config: SSHConfig) -> None:
+        """
+        Args:
+            config: SSH client config
+        """
+        logger_tag = f"ssh | {config.host_name}"
+        if config.port != DEFAULT_SSH_PORT:
+            logger_tag += f':{config.port}'
+
+        # Check if the private key exists
+
+        self.log = logger.create_tagged_trace_logger(logger_tag)
+        self.config = config
+
+    def run(self,
+            command: str,
+            timeout_sec: int = DEFAULT_SSH_TIMEOUT_SEC,
+            connect_retries: int = 3,
+            force_tty: bool = False) -> SSHResult:
+        """Run a command on the device then exit.
+
+        Args:
+            command: String to send to the device.
+            timeout_sec: Seconds to wait for the command to complete.
+            connect_retries: Amount of times to retry connect on fail.
+            force_tty: Force pseudo-terminal allocation.
+
+        Raises:
+            FuchsiaSSHError: if the SSH command returns a non-zero status code
+            FuchsiaSSHTimeout: if there is no response within timeout_sec
+            FuchsiaSSHTransportError: if SSH fails to run the command
+
+        Returns:
+            SSHResults from the executed command.
+        """
+        err: Exception
+        for i in range(0, connect_retries):
+            try:
+                return self._run(command, timeout_sec, force_tty)
+            except FuchsiaSSHTransportError as e:
+                err = e
+                self.log.warn(f'Connect failed: {e}')
+        raise err
+
+    def _run(self, command: str, timeout_sec: int, force_tty: bool) -> SSHResult:
+        full_command = self.config.full_command(command, force_tty)
+        self.log.debug(f'Running "{" ".join(full_command)}"')
+        try:
+            process = subprocess.run(full_command,
+                                     capture_output=True,
+                                     timeout=timeout_sec,
+                                     check=True)
+        except subprocess.CalledProcessError as e:
+            if e.returncode == 255:
+                stderr = e.stderr.decode('utf-8', errors='replace')
+                if 'Name or service not known' in stderr or 'Host does not exist' in stderr:
+                    raise FuchsiaSSHTransportError(
+                        f'Hostname {self.config.host_name} cannot be resolved to an address'
+                    ) from e
+                if 'Connection timed out' in stderr:
+                    raise FuchsiaSSHTransportError(
+                        f'Failed to establish a connection to {self.config.host_name} within {timeout_sec}s'
+                    ) from e
+                if 'Connection refused' in stderr:
+                    raise FuchsiaSSHTransportError(
+                        f'Connection refused by {self.config.host_name}') from e
+
+            raise FuchsiaSSHError(command, SSHResult(e)) from e
+        except subprocess.TimeoutExpired as e:
+            raise SSHTimeout(e) from e
+
+        return SSHResult(process)
+
+    def start_v1_component(self,
+                           component: str,
+                           timeout_sec: int = 5,
+                           repo: str = FUCHSIA_PACKAGE_REPO_NAME) -> None:
+        """Start a CFv1 component in the background.
+
+        Args:
+            component: Name of the component without ".cmx".
+            timeout_sec: Seconds to wait for the process to show up in 'ps'.
+            repo: Default package repository for all components.
+
+        Raises:
+            TimeoutError: when the component doesn't launch within timeout_sec
+        """
+        # The "run -d" command will hang when executed without a pseudo-tty
+        # allocated.
+        self.run(
+            f'run -d fuchsia-pkg://{repo}/{component}#meta/{component}.cmx', force_tty=True)
+
+        timeout = time.perf_counter() + timeout_sec
+        while True:
+            ps_cmd = self.run("ps")
+            if f'{component}.cmx' in ps_cmd.stdout:
+                return
+            if time.perf_counter() > timeout:
+                raise TimeoutError(
+                    f'Failed to start "{component}.cmx" after {timeout_sec}s')
+
+    def stop_v1_component(self, component: str) -> None:
+        """Stop all instances of a CFv1 component.
+
+        Args:
+            component: Name of the component without ".cmx"
+        """
+        try:
+            self.run(f'killall {component}.cmx')
+        except FuchsiaSSHError as e:
+            if 'no tasks found' in e.result.stderr:
+                return
+            raise e
diff --git a/acts/framework/acts/controllers/fuchsia_lib/sysinfo_lib.py b/acts/framework/acts/controllers/fuchsia_lib/sysinfo_lib.py
deleted file mode 100644
index 239b2dc..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/sysinfo_lib.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright 2020 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-from acts.controllers.fuchsia_lib.base_lib import BaseLib
-
-
-class FuchsiaSysInfoLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-
-    def getBoardName(self):
-        """ Gets the board name for the platform we're running on.
-
-        Returns:
-            String, board name, prints an error message if error.
-        """
-        test_cmd = "sysinfo_facade.GetBoardName"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getBoardRevision(self):
-        """ Gets the board revision (hw_id) for the platform we're running on.
-
-        Returns:
-            uint32, revision/hw_id, prints an error message if error.
-        """
-        test_cmd = "sysinfo_facade.GetBoardRevision"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
-
-    def getBootloaderVendor(self):
-        """ Gets the board vendor for the platform we're running on.
-
-        Returns:
-            String, bootloader vendor, prints an error message if error.
-        """
-        test_cmd = "sysinfo_facade.GetBootloaderVendor"
-        test_args = {}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
-
-        return self.send_command(test_id, test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/syslog_lib.py b/acts/framework/acts/controllers/fuchsia_lib/syslog_lib.py
deleted file mode 100644
index 67a850a..0000000
--- a/acts/framework/acts/controllers/fuchsia_lib/syslog_lib.py
+++ /dev/null
@@ -1,233 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright 2019 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-import logging
-import time
-
-from threading import Thread
-
-from acts.libs.logging import log_stream
-from acts.libs.logging.log_stream import LogStyles
-from acts.controllers.android_lib.logcat import TimestampTracker
-from acts.controllers.fuchsia_lib.utils_lib import create_ssh_connection
-
-# paramiko-ng has a log line, line number in 1982 in paramiko/transport.py that
-# presents a ERROR log message that is innocuous but could confuse the user.
-# Therefore by setting the log level to CRITICAL the message is not displayed
-# and everything is recovered as expected.
-logging.getLogger("paramiko").setLevel(logging.CRITICAL)
-
-
-def _log_line_func(log, timestamp_tracker):
-    """Returns a lambda that logs a message to the given logger."""
-
-    def log_line(message):
-        timestamp_tracker.read_output(message)
-        log.info(message)
-
-    return log_line
-
-
-def create_syslog_process(serial,
-                          base_path,
-                          ip_address,
-                          ssh_username,
-                          ssh_config,
-                          ssh_port=22,
-                          extra_params=''):
-    """Creates a FuchsiaSyslogProcess that automatically attempts to reconnect.
-
-    Args:
-        serial: The unique identifier for the device.
-        base_path: The base directory used for syslog file output.
-        ip_address: The ip address of the device to get the syslog.
-        ssh_username: Username for the device for the Fuchsia Device.
-        ssh_config: Location of the ssh_config for connecting to the remote
-            device
-        ssh_port: The ssh port of the Fuchsia device.
-        extra_params: Any additional params to be added to the syslog cmdline.
-
-    Returns:
-        A FuchsiaSyslogProcess object.
-    """
-    logger = log_stream.create_logger('fuchsia_log_%s' % serial,
-                                      base_path=base_path,
-                                      log_styles=(LogStyles.LOG_DEBUG
-                                                  | LogStyles.MONOLITH_LOG))
-    syslog = FuchsiaSyslogProcess(ssh_username, ssh_config, ip_address,
-                                  extra_params, ssh_port)
-    timestamp_tracker = TimestampTracker()
-    syslog.set_on_output_callback(_log_line_func(logger, timestamp_tracker))
-    return syslog
-
-
-class FuchsiaSyslogError(Exception):
-    """Raised when invalid operations are run on a Fuchsia Syslog."""
-
-
-class FuchsiaSyslogProcess(object):
-    """A class representing a Fuchsia Syslog object that communicates over ssh.
-    """
-
-    def __init__(self, ssh_username, ssh_config, ip_address, extra_params,
-                 ssh_port):
-        """
-        Args:
-            ssh_username: The username to connect to Fuchsia over ssh.
-            ssh_config: The ssh config that holds the information to connect to
-            a Fuchsia device over ssh.
-            ip_address: The ip address of the Fuchsia device.
-            ssh_port: The ssh port of the Fuchsia device.
-        """
-        self.ssh_config = ssh_config
-        self.ip_address = ip_address
-        self.extra_params = extra_params
-        self.ssh_username = ssh_username
-        self.ssh_port = ssh_port
-        self._output_file = None
-        self._ssh_client = None
-        self._listening_thread = None
-        self._redirection_thread = None
-        self._on_output_callback = lambda *args, **kw: None
-
-        self._started = False
-        self._stopped = False
-
-    def start(self):
-        """Starts reading the data from the syslog ssh connection."""
-        if self._started:
-            logging.info('Syslog has already started for FuchsiaDevice (%s).' %
-                         self.ip_address)
-            return None
-        self._started = True
-
-        self._listening_thread = Thread(target=self._exec_loop)
-        self._listening_thread.start()
-
-        time_up_at = time.time() + 10
-
-        while self._ssh_client is None:
-            if time.time() > time_up_at:
-                raise FuchsiaSyslogError('Unable to connect to syslog!')
-
-        self._stopped = False
-
-    def stop(self):
-        """Stops listening to the syslog ssh connection and coalesces the
-        threads.
-        """
-        if self._stopped:
-            logging.info('Syslog is already stopped for FuchsiaDevice (%s).' %
-                         self.ip_address)
-            return None
-        self._stopped = True
-
-        try:
-            self._ssh_client.close()
-        except Exception as e:
-            raise e
-        finally:
-            self._join_threads()
-            self._started = False
-            return None
-
-    def _join_threads(self):
-        """Waits for the threads associated with the process to terminate."""
-        if self._listening_thread is not None:
-            if self._redirection_thread is not None:
-                self._redirection_thread.join()
-                self._redirection_thread = None
-
-            self._listening_thread.join()
-            self._listening_thread = None
-
-    def _redirect_output(self):
-        """Redirects the output from the ssh connection into the
-        on_output_callback.
-        """
-        # In some cases, the parent thread (listening_thread) was being joined
-        # before the redirect_thread could finish initiating, meaning it would
-        # run forever attempting to redirect the output even if the listening
-        # thread was torn down. This allows the thread to close at the test
-        # end.
-        parent_listener = self._listening_thread
-        while True:
-            line = self._output_file.readline()
-
-            if not line:
-                return
-            if self._listening_thread != parent_listener:
-                break
-            else:
-                # Output the line without trailing \n and whitespace.
-                self._on_output_callback(line.rstrip())
-
-    def set_on_output_callback(self, on_output_callback, binary=False):
-        """Sets the on_output_callback function.
-
-        Args:
-            on_output_callback: The function to be called when output is sent to
-                the output. The output callback has the following signature:
-
-                >>> def on_output_callback(output_line):
-                >>>     return None
-
-            binary: If True, read the process output as raw binary.
-        Returns:
-            self
-        """
-        self._on_output_callback = on_output_callback
-        self._binary_output = binary
-        return self
-
-    def __start_process(self):
-        """A convenient wrapper function for starting the ssh connection and
-        starting the syslog."""
-
-        self._ssh_client = create_ssh_connection(self.ip_address,
-                                                 self.ssh_username,
-                                                 self.ssh_config,
-                                                 ssh_port=self.ssh_port)
-        transport = self._ssh_client.get_transport()
-        channel = transport.open_session()
-        channel.get_pty()
-        self._output_file = channel.makefile()
-        logging.debug('Starting FuchsiaDevice (%s) syslog over ssh.' %
-                      self.ssh_username)
-        channel.exec_command('log_listener %s' % self.extra_params)
-        return transport
-
-    def _exec_loop(self):
-        """Executes a ssh connection to the Fuchsia Device syslog in a loop.
-
-        When the ssh connection terminates without stop() being called,
-        the threads are coalesced and the syslog is restarted.
-        """
-        start_up = True
-        while True:
-            if self._stopped:
-                break
-            if start_up:
-                ssh_transport = self.__start_process()
-                self._redirection_thread = Thread(target=self._redirect_output)
-                self._redirection_thread.start()
-                start_up = False
-            else:
-                if not ssh_transport.is_alive():
-                    if self._redirection_thread is not None:
-                        self._redirection_thread.join()
-                        self._redirection_thread = None
-                    self.start_up = True
diff --git a/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py b/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py
index 22b9d50..c83abfc 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py
@@ -14,10 +14,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import backoff
 import os
 import logging
-import paramiko
 import psutil
 import socket
 import tarfile
@@ -26,17 +24,10 @@
 import usbinfo
 
 from acts import utils
-from acts.controllers.fuchsia_lib.base_lib import DeviceOffline
+from acts.controllers.fuchsia_lib.ssh import FuchsiaSSHError
 from acts.libs.proc import job
 from acts.utils import get_fuchsia_mdns_ipv6_address
 
-logging.getLogger("paramiko").setLevel(logging.WARNING)
-# paramiko-ng will throw INFO messages when things get disconnect or cannot
-# connect perfectly the first time.  In this library those are all handled by
-# either retrying and/or throwing an exception for the appropriate case.
-# Therefore, in order to reduce confusion in the logs the log level is set to
-# WARNING.
-
 MDNS_LOOKUP_RETRY_MAX = 3
 FASTBOOT_TIMEOUT = 30
 AFTER_FLASH_BOOT_TIME = 30
@@ -47,156 +38,7 @@
 FUCHSIA_RELEASE_TESTING_URL = "gs://fuchsia-release-testing/images"
 
 
-def get_private_key(ip_address, ssh_config):
-    """Tries to load various ssh key types.
-
-    Args:
-        ip_address: IP address of ssh server.
-        ssh_config: ssh_config location for the ssh server.
-    Returns:
-        The ssh private key
-    """
-    exceptions = []
-    try:
-        logging.debug('Trying to load SSH key type: ed25519')
-        return paramiko.ed25519key.Ed25519Key(
-            filename=get_ssh_key_for_host(ip_address, ssh_config))
-    except paramiko.SSHException as e:
-        exceptions.append(e)
-        logging.debug('Failed loading SSH key type: ed25519')
-
-    try:
-        logging.debug('Trying to load SSH key type: rsa')
-        return paramiko.RSAKey.from_private_key_file(
-            filename=get_ssh_key_for_host(ip_address, ssh_config))
-    except paramiko.SSHException as e:
-        exceptions.append(e)
-        logging.debug('Failed loading SSH key type: rsa')
-
-    raise Exception('No valid ssh key type found', exceptions)
-
-
-@backoff.on_exception(
-    backoff.constant,
-    (paramiko.ssh_exception.SSHException,
-     paramiko.ssh_exception.AuthenticationException, socket.timeout,
-     socket.error, ConnectionRefusedError, ConnectionResetError),
-    interval=1.5,
-    max_tries=4)
-def create_ssh_connection(ip_address,
-                          ssh_username,
-                          ssh_config,
-                          ssh_port=22,
-                          connect_timeout=10,
-                          auth_timeout=10,
-                          banner_timeout=10):
-    """Creates and ssh connection to a Fuchsia device
-
-    Args:
-        ip_address: IP address of ssh server.
-        ssh_username: Username for ssh server.
-        ssh_config: ssh_config location for the ssh server.
-        ssh_port: port for the ssh server.
-        connect_timeout: Timeout value for connecting to ssh_server.
-        auth_timeout: Timeout value to wait for authentication.
-        banner_timeout: Timeout to wait for ssh banner.
-
-    Returns:
-        A paramiko ssh object
-    """
-    if not utils.can_ping(job, ip_address):
-        raise DeviceOffline("Device %s is not reachable via "
-                            "the network." % ip_address)
-    ssh_key = get_private_key(ip_address=ip_address, ssh_config=ssh_config)
-    ssh_client = paramiko.SSHClient()
-    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
-    ssh_client.connect(hostname=ip_address,
-                       username=ssh_username,
-                       allow_agent=False,
-                       pkey=ssh_key,
-                       port=ssh_port,
-                       timeout=connect_timeout,
-                       auth_timeout=auth_timeout,
-                       banner_timeout=banner_timeout)
-    ssh_client.get_transport().set_keepalive(1)
-    return ssh_client
-
-
-def ssh_is_connected(ssh_client):
-    """Checks to see if the SSH connection is alive.
-    Args:
-        ssh_client: A paramiko SSH client instance.
-    Returns:
-          True if connected, False or None if not connected.
-    """
-    return ssh_client and ssh_client.get_transport().is_active()
-
-
-def get_ssh_key_for_host(host, ssh_config_file):
-    """Gets the SSH private key path from a supplied ssh_config_file and the
-       host.
-    Args:
-        host (str): The ip address or host name that SSH will connect to.
-        ssh_config_file (str): Path to the ssh_config_file that will be used
-            to connect to the host.
-
-    Returns:
-        path: A path to the private key for the SSH connection.
-    """
-    ssh_config = paramiko.SSHConfig()
-    user_config_file = os.path.expanduser(ssh_config_file)
-    if os.path.exists(user_config_file):
-        with open(user_config_file) as f:
-            ssh_config.parse(f)
-    user_config = ssh_config.lookup(host)
-
-    if 'identityfile' not in user_config:
-        raise ValueError('Could not find identity file in %s.' % ssh_config)
-
-    path = os.path.expanduser(user_config['identityfile'][0])
-    if not os.path.exists(path):
-        raise FileNotFoundError('Specified IdentityFile %s for %s in %s not '
-                                'existing anymore.' % (path, host, ssh_config))
-    return path
-
-
-class SshResults:
-    """Class representing the results from a SSH command to mimic the output
-    of the job.Result class in ACTS.  This is to reduce the changes needed from
-    swapping the ssh connection in ACTS to paramiko.
-
-    Attributes:
-        stdin: The file descriptor to the input channel of the SSH connection.
-        stdout: The file descriptor to the stdout of the SSH connection.
-        stderr: The file descriptor to the stderr of the SSH connection.
-        exit_status: The file descriptor of the SSH command.
-    """
-
-    def __init__(self, stdin, stdout, stderr, exit_status):
-        self._raw_stdout = stdout.read()
-        self._stdout = self._raw_stdout.decode('utf-8', errors='replace')
-        self._stderr = stderr.read().decode('utf-8', errors='replace')
-        self._exit_status = exit_status.recv_exit_status()
-
-    @property
-    def stdout(self):
-        return self._stdout
-
-    @property
-    def raw_stdout(self):
-        return self._raw_stdout
-
-    @property
-    def stderr(self):
-        return self._stderr
-
-    @property
-    def exit_status(self):
-        return self._exit_status
-
-
-def flash(fuchsia_device,
-          use_ssh=False,
+def flash(fuchsia_device, use_ssh=False,
           fuchsia_reconnect_after_reboot_time=5):
     """A function to flash, not pave, a fuchsia_device
 
@@ -221,10 +63,10 @@
                          'fuchsia_devices.')
     if not fuchsia_device.build_number:
         fuchsia_device.build_number = 'LATEST'
-    if (utils.is_valid_ipv4_address(fuchsia_device.orig_ip)
-            or utils.is_valid_ipv6_address(fuchsia_device.orig_ip)):
-        raise ValueError('The fuchsia_device ip must be the mDNS name to be '
-                         'able to flash.')
+    if not fuchsia_device.mdns_name:
+        raise ValueError(
+            'Either fuchsia_device mdns_name must be specified or '
+            'ip must be the mDNS name to be able to flash.')
 
     file_to_download = None
     image_archive_path = None
@@ -262,7 +104,7 @@
         reboot_to_bootloader(fuchsia_device, use_ssh,
                              fuchsia_reconnect_after_reboot_time)
         logging.info(
-            f'Flashing {fuchsia_device.orig_ip} with {image_path} using authorized keys "{fuchsia_device.authorized_file}".'
+            f'Flashing {fuchsia_device.mdns_name} with {image_path} using authorized keys "{fuchsia_device.authorized_file}".'
         )
         run_flash_script(fuchsia_device, image_path)
     else:
@@ -283,7 +125,7 @@
                                  fuchsia_reconnect_after_reboot_time)
 
             logging.info(
-                f'Flashing {fuchsia_device.orig_ip} with {image_archive_path} using authorized keys "{fuchsia_device.authorized_file}".'
+                f'Flashing {fuchsia_device.mdns_name} with {image_archive_path} using authorized keys "{fuchsia_device.authorized_file}".'
             )
             run_flash_script(fuchsia_device, image_path)
     return True
@@ -295,17 +137,17 @@
     if use_ssh:
         logging.info('Sending reboot command via SSH to '
                      'get into bootloader.')
-        with utils.SuppressLogOutput():
-            fuchsia_device.clean_up_services()
-            # Sending this command will put the device in fastboot
-            # but it does not guarantee the device will be in fastboot
-            # after this command.  There is no check so if there is an
-            # expectation of the device being in fastboot, then some
-            # other check needs to be done.
-            fuchsia_device.send_command_ssh(
-                'dm rb',
-                timeout=fuchsia_reconnect_after_reboot_time,
-                skip_status_code_check=True)
+        # Sending this command will put the device in fastboot
+        # but it does not guarantee the device will be in fastboot
+        # after this command.  There is no check so if there is an
+        # expectation of the device being in fastboot, then some
+        # other check needs to be done.
+        try:
+            fuchsia_device.ssh.run(
+                'dm rb', timeout_sec=fuchsia_reconnect_after_reboot_time)
+        except FuchsiaSSHError as e:
+            if 'closed by remote host' not in e.result.stderr:
+                raise e
     else:
         pass
         ## Todo: Add elif for SL4F if implemented in SL4F
@@ -314,14 +156,14 @@
     while time_counter < FASTBOOT_TIMEOUT:
         logging.info('Checking to see if fuchsia_device(%s) SN: %s is in '
                      'fastboot. (Attempt #%s Timeout: %s)' %
-                     (fuchsia_device.orig_ip, fuchsia_device.serial_number,
+                     (fuchsia_device.mdns_name, fuchsia_device.serial_number,
                       str(time_counter + 1), FASTBOOT_TIMEOUT))
         for usb_device in usbinfo.usbinfo():
             if (usb_device['iSerialNumber'] == fuchsia_device.serial_number
                     and usb_device['iProduct'] == 'USB_download_gadget'):
                 logging.info(
                     'fuchsia_device(%s) SN: %s is in fastboot.' %
-                    (fuchsia_device.orig_ip, fuchsia_device.serial_number))
+                    (fuchsia_device.mdns_name, fuchsia_device.serial_number))
                 time_counter = FASTBOOT_TIMEOUT
         time_counter = time_counter + 1
         if time_counter == FASTBOOT_TIMEOUT:
@@ -330,7 +172,7 @@
             raise TimeoutError(
                 'fuchsia_device(%s) SN: %s '
                 'never went into fastboot' %
-                (fuchsia_device.orig_ip, fuchsia_device.serial_number))
+                (fuchsia_device.mdns_name, fuchsia_device.serial_number))
         time.sleep(1)
 
     end_time = time.time() + WAIT_FOR_EXISTING_FLASH_TO_FINISH_SEC
@@ -362,21 +204,44 @@
     logging.info('Updating device to new IP addresses.')
     mdns_ip = None
     for retry_counter in range(MDNS_LOOKUP_RETRY_MAX):
-        mdns_ip = get_fuchsia_mdns_ipv6_address(fuchsia_device.orig_ip)
+        mdns_ip = get_fuchsia_mdns_ipv6_address(fuchsia_device.mdns_name)
         if mdns_ip:
             break
         else:
             time.sleep(1)
     if mdns_ip and utils.is_valid_ipv6_address(mdns_ip):
         logging.info('IP for fuchsia_device(%s) changed from %s to %s' %
-                     (fuchsia_device.orig_ip, fuchsia_device.ip, mdns_ip))
+                     (fuchsia_device.mdns_name, fuchsia_device.ip, mdns_ip))
         fuchsia_device.ip = mdns_ip
         fuchsia_device.address = "http://[{}]:{}".format(
             fuchsia_device.ip, fuchsia_device.sl4f_port)
-        fuchsia_device.init_address = fuchsia_device.address + "/init"
-        fuchsia_device.cleanup_address = fuchsia_device.address + "/cleanup"
-        fuchsia_device.print_address = fuchsia_device.address + "/print_clients"
-        fuchsia_device.init_libraries()
     else:
         raise ValueError('Invalid IP: %s after flashing.' %
-                         fuchsia_device.orig_ip)
+                         fuchsia_device.mdns_name)
+
+
+def wait_for_port(host: str, port: int, timeout_sec: int = 5) -> None:
+    """Wait for the host to start accepting connections on the port.
+
+    Some services take some time to start. Call this after launching the service
+    to avoid race conditions.
+
+    Args:
+        host: IP of the running service.
+        port: Port of the running service.
+        timeout_sec: Seconds to wait until raising TimeoutError
+
+    Raises:
+        TimeoutError: when timeout_sec has expired without a successful
+            connection to the service
+    """
+    timeout = time.perf_counter() + timeout_sec
+    while True:
+        try:
+            with socket.create_connection((host, port), timeout=timeout_sec):
+                return
+        except ConnectionRefusedError as e:
+            if time.perf_counter() > timeout:
+                raise TimeoutError(
+                    f'Waited over {timeout_sec}s for the service to start '
+                    f'accepting connections at {host}:{port}') from e
diff --git a/acts/framework/acts/controllers/fuchsia_lib/wlan_ap_policy_lib.py b/acts/framework/acts/controllers/fuchsia_lib/wlan_ap_policy_lib.py
index 46b9bb7..b8dad9f 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/wlan_ap_policy_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/wlan_ap_policy_lib.py
@@ -18,10 +18,9 @@
 
 
 class FuchsiaWlanApPolicyLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "wlan_ap_policy")
 
     def wlanStartAccessPoint(self, target_ssid, security_type, target_pwd,
                              connectivity_mode, operating_band):
@@ -42,8 +41,6 @@
         """
 
         test_cmd = "wlan_ap_policy.start_access_point"
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
         test_args = {
             "target_ssid": target_ssid,
@@ -53,7 +50,7 @@
             "operating_band": operating_band,
         }
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanStopAccessPoint(self, target_ssid, security_type, target_pwd=""):
         """ Stops an active Access Point.
@@ -68,8 +65,6 @@
         """
 
         test_cmd = "wlan_ap_policy.stop_access_point"
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
         test_args = {
             "target_ssid": target_ssid,
@@ -77,7 +72,7 @@
             "target_pwd": target_pwd
         }
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanStopAllAccessPoint(self):
         """ Stops all Access Points
@@ -87,12 +82,10 @@
         """
 
         test_cmd = "wlan_ap_policy.stop_all_access_points"
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
         test_args = {}
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanSetNewListener(self):
         """ Sets the update listener stream of the facade to a new stream so that updates will be
@@ -100,10 +93,8 @@
             independent from previous tests.
         """
         test_cmd = "wlan_ap_policy.set_new_update_listener"
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanGetUpdate(self, timeout=30):
         """ Gets a list of AP state updates. This call will return with an update immediately the
@@ -115,9 +106,5 @@
                 structure that matches the FIDL AccessPointState struct given for updates.
         """
         test_cmd = "wlan_ap_policy.get_update"
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id,
-                                 test_cmd, {},
-                                 response_timeout=timeout)
+        return self.send_command(test_cmd, {}, response_timeout=timeout)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/wlan_deprecated_configuration_lib.py b/acts/framework/acts/controllers/fuchsia_lib/wlan_deprecated_configuration_lib.py
index a13e6f2..1e22627 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/wlan_deprecated_configuration_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/wlan_deprecated_configuration_lib.py
@@ -17,15 +17,11 @@
 from acts import logger
 from acts.controllers.fuchsia_lib.base_lib import BaseLib
 
-COMMAND_SUGGEST_AP_MAC_ADDR = 'wlan_deprecated.suggest_ap_mac'
-
 
 class FuchsiaWlanDeprecatedConfigurationLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-        self.log = logger.create_tagged_trace_logger(str(addr))
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "wlan_deprecated")
 
     def wlanSuggestAccessPointMacAddress(self, addr):
         """ Suggests a mac address to soft AP interface, to support
@@ -34,9 +30,7 @@
         Args:
             addr: string of mac address to suggest (e.g. '12:34:56:78:9a:bc')
         """
-        test_cmd = COMMAND_SUGGEST_AP_MAC_ADDR
+        test_cmd = 'wlan_deprecated.suggest_ap_mac'
         test_args = {'mac': addr}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
\ No newline at end of file
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/wlan_lib.py b/acts/framework/acts/controllers/fuchsia_lib/wlan_lib.py
index d3dc448..9fe93b1 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/wlan_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/wlan_lib.py
@@ -30,11 +30,9 @@
 
 
 class FuchsiaWlanLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-        self.log = logger.create_tagged_trace_logger(str(addr))
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "wlan")
 
     def wlanStartScan(self):
         """ Starts a wlan scan
@@ -43,10 +41,8 @@
             scan results
         """
         test_cmd = COMMAND_SCAN
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanScanForBSSInfo(self):
         """ Scans and returns BSS info
@@ -56,10 +52,8 @@
             blocks, one for each BSS observed in the network
         """
         test_cmd = COMMAND_SCAN_FOR_BSS_INFO
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanConnectToNetwork(self,
                              target_ssid,
@@ -79,18 +73,14 @@
             "target_pwd": target_pwd,
             "target_bss_desc": target_bss_desc
         }
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanDisconnect(self):
         """ Disconnect any current wifi connections"""
         test_cmd = COMMAND_DISCONNECT
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanDestroyIface(self, iface_id):
         """ Destroy WLAN interface by ID.
@@ -102,10 +92,8 @@
         """
         test_cmd = COMMAND_DESTROY_IFACE
         test_args = {"identifier": iface_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanGetIfaceIdList(self):
         """ Get a list if wlan interface IDs.
@@ -114,10 +102,8 @@
             Dictionary, service id if success, error if error.
         """
         test_cmd = COMMAND_GET_IFACE_ID_LIST
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanPhyIdList(self):
         """ Get a list if wlan phy IDs.
@@ -126,10 +112,8 @@
             List of IDs if success, error if error.
         """
         test_cmd = COMMAND_GET_PHY_ID_LIST
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanStatus(self, iface_id=None):
         """ Request connection status
@@ -146,10 +130,8 @@
         test_args = {}
         if iface_id:
             test_args = {'iface_id': iface_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanGetCountry(self, phy_id):
         """ Reads the currently configured country for `phy_id`.
@@ -162,10 +144,8 @@
         """
         test_cmd = COMMAND_GET_COUNTRY
         test_args = {"phy_id": phy_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanGetDevPath(self, phy_id):
         """ Queries the device path for `phy_id`.
@@ -178,10 +158,8 @@
         """
         test_cmd = COMMAND_GET_DEV_PATH
         test_args = {"phy_id": phy_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanQueryInterface(self, iface_id):
         """ Retrieves interface info for given wlan iface id.
@@ -195,7 +173,5 @@
         """
         test_cmd = COMMAND_QUERY_IFACE
         test_args = {'iface_id': iface_id}
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/wlan_policy_lib.py b/acts/framework/acts/controllers/fuchsia_lib/wlan_policy_lib.py
index 03ab555..720a6fa 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/wlan_policy_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/wlan_policy_lib.py
@@ -34,39 +34,24 @@
 COMMAND_GET_UPDATE = "wlan_policy.get_update"
 
 
-def main(argv):
-    if len(argv) > 1:
-        raise app.UsageError('Too many command-line arguments.')
-
-
-if __name__ == '__main__':
-    app.run(main)
-
-
 class FuchsiaWlanPolicyLib(BaseLib):
-    def __init__(self, addr, tc, client_id):
-        self.address = addr
-        self.test_counter = tc
-        self.client_id = client_id
-        self.log = logger.create_tagged_trace_logger(str(addr))
+
+    def __init__(self, addr: str) -> None:
+        super().__init__(addr, "wlan_policy")
 
     def wlanStartClientConnections(self):
         """ Enables device to initiate connections to networks """
 
         test_cmd = COMMAND_START_CLIENT_CONNECTIONS
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanStopClientConnections(self):
         """ Disables device for initiating connections to networks """
 
         test_cmd = COMMAND_STOP_CLIENT_CONNECTIONS
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanScanForNetworks(self):
         """ Scans for networks that can be connected to
@@ -75,10 +60,8 @@
          """
 
         test_cmd = COMMAND_SCAN_FOR_NETWORKS
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanSaveNetwork(self, target_ssid, security_type, target_pwd=None):
         """ Saveds a network to the device for future connections
@@ -94,15 +77,13 @@
         if not target_pwd:
             target_pwd = ''
         test_cmd = COMMAND_SAVE_NETWORK
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
         test_args = {
             "target_ssid": target_ssid,
             "security_type": str(security_type).lower(),
             "target_pwd": target_pwd
         }
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanRemoveNetwork(self, target_ssid, security_type, target_pwd=None):
         """ Removes or "forgets" a network from saved networks
@@ -115,15 +96,13 @@
         if not target_pwd:
             target_pwd = ''
         test_cmd = COMMAND_REMOVE_NETWORK
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
         test_args = {
             "target_ssid": target_ssid,
             "security_type": str(security_type).lower(),
             "target_pwd": target_pwd
         }
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanRemoveAllNetworks(self):
         """ Removes or "forgets" all networks from saved networks
@@ -132,10 +111,8 @@
         """
 
         test_cmd = COMMAND_REMOVE_ALL_NETWORKS
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanGetSavedNetworks(self):
         """ Gets networks saved on device. Any PSK of a saved network will be
@@ -145,10 +122,8 @@
         """
 
         test_cmd = COMMAND_GET_SAVED_NETWORKS
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanConnect(self, target_ssid, security_type):
         """ Triggers connection to a network
@@ -162,24 +137,20 @@
         """
 
         test_cmd = COMMAND_CONNECT
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
         test_args = {
             "target_ssid": target_ssid,
             "security_type": str(security_type).lower()
         }
 
-        return self.send_command(test_id, test_cmd, test_args)
+        return self.send_command(test_cmd, test_args)
 
     def wlanCreateClientController(self):
         """ Initializes the client controller of the facade that is used to make Client Controller
             API calls
         """
         test_cmd = COMMAND_CREATE_CLIENT_CONTROLLER
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanSetNewListener(self):
         """ Sets the update listener stream of the facade to a new stream so that updates will be
@@ -187,20 +158,16 @@
             independent from previous tests.
         """
         test_cmd = COMMAND_SET_NEW_LISTENER
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanRemoveAllNetworks(self):
         """ Deletes all saved networks on the device. Relies directly on the get_saved_networks and
             remove_network commands
         """
         test_cmd = COMMAND_REMOVE_ALL_NETWORKS
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id, test_cmd, {})
+        return self.send_command(test_cmd, {})
 
     def wlanGetUpdate(self, timeout=30):
         """ Gets one client listener update. This call will return with an update immediately the
@@ -212,9 +179,5 @@
                 structure that matches the FIDL ClientStateSummary struct given for updates.
         """
         test_cmd = COMMAND_GET_UPDATE
-        test_id = self.build_id(self.test_counter)
-        self.test_counter += 1
 
-        return self.send_command(test_id,
-                                 test_cmd, {},
-                                 response_timeout=timeout)
\ No newline at end of file
+        return self.send_command(test_cmd, {}, response_timeout=timeout)
diff --git a/acts/framework/acts/controllers/handover_simulator.py b/acts/framework/acts/controllers/handover_simulator.py
new file mode 100644
index 0000000..57fb3f8
--- /dev/null
+++ b/acts/framework/acts/controllers/handover_simulator.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""Abstract simulator for providing cellular handover functionality."""
+
+from enum import Enum
+
+
+class CellularTechnology(Enum):
+    """A supported cellular technology to handover to/from."""
+    LTE = 'LTE'
+    WCDMA = 'WCDMA'
+
+
+class AbstractHandoverSimulator:
+    """Simulator for facilitating inter/intra-RAT handovers."""
+
+    def lte_handover(self, band, channel, bandwidth, source_technology):
+        """Performs a handover to LTE.
+
+        Args:
+            band: the band of the handover destination
+            channel: the downlink channel of the handover destination
+            bandwidth: the downlink bandwidth of the handover destination
+            source_technology: the source handover technology.
+        """
+        raise NotImplementedError()
+
+    def wcdma_handover(self, band, channel, source_technology):
+        """Performs a handover to WCDMA.
+
+        Args:
+            band: the band of the handover destination
+            channel: the downlink channel of the handover destination
+            source_technology: the source handover technology.
+        """
+        raise NotImplementedError()
+
+
+class HandoverSimulatorError(Exception):
+    """Exceptions thrown when the cellular equipment is unable to successfully perform a handover operation."""
diff --git a/acts/framework/acts/controllers/iperf_client.py b/acts/framework/acts/controllers/iperf_client.py
index 6c889b1..95e7965 100644
--- a/acts/framework/acts/controllers/iperf_client.py
+++ b/acts/framework/acts/controllers/iperf_client.py
@@ -24,19 +24,14 @@
 from acts import utils
 from acts.controllers.adb_lib.error import AdbCommandError
 from acts.controllers.android_device import AndroidDevice
+from acts.controllers.fuchsia_lib.ssh import SSHProvider
 from acts.controllers.iperf_server import _AndroidDeviceBridge
-from acts.controllers.fuchsia_lib.utils_lib import create_ssh_connection
-from acts.controllers.fuchsia_lib.utils_lib import ssh_is_connected
-from acts.controllers.fuchsia_lib.utils_lib import SshResults
 from acts.controllers.utils_lib.ssh import connection
 from acts.controllers.utils_lib.ssh import settings
-from acts.event import event_bus
-from acts.event.decorators import subscribe_static
-from acts.event.event import TestClassBeginEvent
-from acts.event.event import TestClassEndEvent
 from acts.libs.proc import job
 from paramiko.buffered_pipe import PipeTimeout
 from paramiko.ssh_exception import SSHException
+
 MOBLY_CONTROLLER_CONFIG_NAME = 'IPerfClient'
 ACTS_CONTROLLER_REFERENCE_NAME = 'iperf_clients'
 
@@ -65,7 +60,6 @@
         elif type(c) is dict and 'ssh_config' in c:
             results.append(
                 IPerfClientOverSsh(c['ssh_config'],
-                                   use_paramiko=c.get('use_paramiko'),
                                    test_interface=c.get('test_interface')))
         else:
             results.append(IPerfClient())
@@ -142,6 +136,7 @@
 
 class IPerfClient(IPerfClientBase):
     """Class that handles iperf3 client operations."""
+
     def start(self, ip, iperf_args, tag, timeout=3600, iperf_binary=None):
         """Starts iperf client, and waits for completion.
 
@@ -174,21 +169,23 @@
 
 class IPerfClientOverSsh(IPerfClientBase):
     """Class that handles iperf3 client operations on remote machines."""
-    def __init__(self, ssh_config, use_paramiko=False, test_interface=None):
-        self._ssh_settings = settings.from_config(ssh_config)
-        if not (utils.is_valid_ipv4_address(self._ssh_settings.hostname)
-                or utils.is_valid_ipv6_address(self._ssh_settings.hostname)):
-            mdns_ip = utils.get_fuchsia_mdns_ipv6_address(
-                self._ssh_settings.hostname)
-            if mdns_ip:
-                self._ssh_settings.hostname = mdns_ip
-        # use_paramiko may be passed in as a string (from JSON), so this line
-        # guarantees it is a converted to a bool.
-        self._use_paramiko = str(use_paramiko).lower() == 'true'
+
+    def __init__(self,
+                 ssh_config: str,
+                 test_interface: str = None,
+                 ssh_provider: SSHProvider = None):
+        self._ssh_provider = ssh_provider
+        if not self._ssh_provider:
+            self._ssh_settings = settings.from_config(ssh_config)
+            if not (utils.is_valid_ipv4_address(self._ssh_settings.hostname) or
+                    utils.is_valid_ipv6_address(self._ssh_settings.hostname)):
+                mdns_ip = utils.get_fuchsia_mdns_ipv6_address(
+                    self._ssh_settings.hostname)
+                if mdns_ip:
+                    self._ssh_settings.hostname = mdns_ip
         self._ssh_session = None
         self.start_ssh()
 
-        self.hostname = self._ssh_settings.hostname
         self.test_interface = test_interface
 
     def start(self, ip, iperf_args, tag, timeout=3600, iperf_binary=None):
@@ -216,22 +213,10 @@
         full_out_path = self._get_full_file_path(tag)
 
         try:
-            if not self._ssh_session:
-                self.start_ssh()
-            if self._use_paramiko:
-                if not ssh_is_connected(self._ssh_session):
-                    logging.info('Lost SSH connection to %s. Reconnecting.' %
-                                 self._ssh_settings.hostname)
-                    self._ssh_session.close()
-                    self._ssh_session = create_ssh_connection(
-                        ip_address=self._ssh_settings.hostname,
-                        ssh_username=self._ssh_settings.username,
-                        ssh_config=self._ssh_settings.ssh_config)
-                cmd_result_stdin, cmd_result_stdout, cmd_result_stderr = (
-                    self._ssh_session.exec_command(iperf_cmd, timeout=timeout))
-                iperf_process = SshResults(cmd_result_stdin, cmd_result_stdout,
-                                           cmd_result_stderr,
-                                           cmd_result_stdout.channel)
+            self.start_ssh()
+            if self._ssh_provider:
+                iperf_process = self._ssh_provider.run(iperf_cmd,
+                                                       timeout_sec=timeout)
             else:
                 iperf_process = self._ssh_session.run(iperf_cmd,
                                                       timeout=timeout)
@@ -253,15 +238,11 @@
 
     def start_ssh(self):
         """Starts an ssh session to the iperf client."""
+        if self._ssh_provider:
+            # SSH sessions are created by the provider.
+            return
         if not self._ssh_session:
-            if self._use_paramiko:
-                self._ssh_session = create_ssh_connection(
-                    ip_address=self._ssh_settings.hostname,
-                    ssh_username=self._ssh_settings.username,
-                    ssh_config=self._ssh_settings.ssh_config)
-            else:
-                self._ssh_session = connection.SshConnection(
-                    self._ssh_settings)
+            self._ssh_session = connection.SshConnection(self._ssh_settings)
 
     def close_ssh(self):
         """Closes the ssh session to the iperf client, if one exists, preventing
@@ -274,6 +255,7 @@
 
 class IPerfClientOverAdb(IPerfClientBase):
     """Class that handles iperf3 operations over ADB devices."""
+
     def __init__(self, android_device_or_serial, test_interface=None):
         """Creates a new IPerfClientOverAdb object.
 
diff --git a/acts/framework/acts/controllers/iperf_server.py b/acts/framework/acts/controllers/iperf_server.py
index 4be9d1e..231e9a8 100755
--- a/acts/framework/acts/controllers/iperf_server.py
+++ b/acts/framework/acts/controllers/iperf_server.py
@@ -24,6 +24,7 @@
 import time
 
 from acts import context
+from acts import logger as acts_logger
 from acts import utils
 from acts.controllers.android_device import AndroidDevice
 from acts.controllers.utils_lib.ssh import connection
@@ -90,6 +91,7 @@
 
 
 class IPerfResult(object):
+
     def __init__(self, result_path, reporting_speed_units='Mbytes'):
         """Loads iperf result from file.
 
@@ -342,6 +344,7 @@
 
 class IPerfServer(IPerfServerBase):
     """Class that handles iperf server commands on localhost."""
+
     def __init__(self, port=5201):
         super().__init__(port)
         self._hinted_port = port
@@ -419,6 +422,7 @@
 
 class IPerfServerOverSsh(IPerfServerBase):
     """Class that handles iperf3 operations on remote machines."""
+
     def __init__(self,
                  ssh_config,
                  port,
@@ -426,6 +430,8 @@
                  use_killall=False):
         super().__init__(port)
         self.ssh_settings = settings.from_config(ssh_config)
+        self.log = acts_logger.create_tagged_trace_logger(
+            f'IPerfServer | {self.ssh_settings.hostname}')
         self._ssh_session = None
         self.start_ssh()
 
@@ -437,9 +443,10 @@
             # A test interface can only be found if an ip address is specified.
             # A fully qualified hostname will return None for the
             # test_interface.
-            self.test_interface = self._get_test_interface_based_on_ip(
-                test_interface)
-        except Exception:
+            self.test_interface = test_interface if test_interface else utils.get_interface_based_on_ip(
+                self._ssh_session, self.hostname)
+        except Exception as e:
+            self.log.warning(e)
             self.test_interface = None
 
     @property
@@ -453,21 +460,6 @@
     def _get_remote_log_path(self):
         return '/tmp/iperf_server_port%s.log' % self.port
 
-    def _get_test_interface_based_on_ip(self, test_interface):
-        """Gets the test interface for a particular IP if the test interface
-            passed in test_interface is None
-
-        Args:
-            test_interface: Either a interface name, ie eth0, or None
-
-        Returns:
-            The name of the test interface.
-        """
-        if test_interface:
-            return test_interface
-        return utils.get_interface_based_on_ip(self._ssh_session,
-                                               self.hostname)
-
     def get_interface_ip_addresses(self, interface):
         """Gets all of the ip addresses, ipv4 and ipv6, associated with a
            particular interface name.
@@ -476,13 +468,8 @@
             interface: The interface name on the device, ie eth0
 
         Returns:
-            A list of dictionaries of the the various IP addresses:
-                ipv4_private_local_addresses: Any 192.168, 172.16, or 10
-                    addresses
-                ipv4_public_addresses: Any IPv4 public addresses
-                ipv6_link_local_addresses: Any fe80:: addresses
-                ipv6_private_local_addresses: Any fd00:: addresses
-                ipv6_public_addresses: Any publicly routable addresses
+            A list of dictionaries of the various IP addresses. See
+            utils.get_interface_ip_addresses.
         """
         if not self._ssh_session:
             self.start_ssh()
@@ -490,13 +477,23 @@
         return utils.get_interface_ip_addresses(self._ssh_session, interface)
 
     def renew_test_interface_ip_address(self):
-        """Renews the test interface's IP address.  Necessary for changing
-           DHCP scopes during a test.
+        """Renews the test interface's IPv4 address.
+
+        Necessary for changing DHCP scopes during a test.
         """
         if not self._ssh_session:
             self.start_ssh()
         utils.renew_linux_ip_address(self._ssh_session, self.test_interface)
 
+    def get_addr(self, addr_type='ipv4_private', timeout_sec=None):
+        """Wait until a type of IP address on the test interface is available
+        then return it.
+        """
+        if not self._ssh_session:
+            self.start_ssh()
+        return utils.get_addr(self._ssh_session, self.test_interface,
+                              addr_type, timeout_sec)
+
     def _cleanup_iperf_port(self):
         """Checks and kills zombie iperf servers occupying intended port."""
         iperf_check_cmd = ('netstat -tulpn | grep LISTEN | grep iperf3'
@@ -623,6 +620,7 @@
 
 class IPerfServerOverAdb(IPerfServerBase):
     """Class that handles iperf3 operations over ADB devices."""
+
     def __init__(self, android_device_or_serial, port):
         """Creates a new IPerfServerOverAdb object.
 
diff --git a/acts/framework/acts/controllers/monsoon_lib/sampling/engine/assembly_line.py b/acts/framework/acts/controllers/monsoon_lib/sampling/engine/assembly_line.py
index 7f4bab2..1643b98 100644
--- a/acts/framework/acts/controllers/monsoon_lib/sampling/engine/assembly_line.py
+++ b/acts/framework/acts/controllers/monsoon_lib/sampling/engine/assembly_line.py
@@ -316,15 +316,12 @@
 
     def initialize(self):
         """Does nothing. Nothing to initialize."""
-        pass
 
     def end_stream(self):
         """Does nothing. The stream always returns end-of-stream when read."""
-        pass
 
     def add_indexed_buffer(self, buffer):
         """Imitating /dev/null, nothing will be written to the stream."""
-        pass
 
     def remove_indexed_buffer(self):
         """Always returns the end-of-stream marker."""
diff --git a/acts/framework/acts/controllers/monsoon_lib/sampling/engine/transformer.py b/acts/framework/acts/controllers/monsoon_lib/sampling/engine/transformer.py
index ca9e4fb..d9c297a 100644
--- a/acts/framework/acts/controllers/monsoon_lib/sampling/engine/transformer.py
+++ b/acts/framework/acts/controllers/monsoon_lib/sampling/engine/transformer.py
@@ -102,11 +102,9 @@
 
     def on_begin(self):
         """A function called before the transform loop begins."""
-        pass
 
     def on_end(self):
         """A function called after the transform loop has ended."""
-        pass
 
 
 class SourceTransformer(Transformer):
diff --git a/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/stock_transformers.py b/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/stock_transformers.py
index bcab7f5..6fd00c4 100644
--- a/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/stock_transformers.py
+++ b/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/stock_transformers.py
@@ -153,7 +153,6 @@
 
     def _transform_buffer(self, buffer):
         """_transform is overloaded, so this function can be left empty."""
-        pass
 
 
 class PacketReader(ParallelTransformer):
diff --git a/acts/framework/acts/controllers/native.py b/acts/framework/acts/controllers/native.py
index a3ac550..6b72397 100644
--- a/acts/framework/acts/controllers/native.py
+++ b/acts/framework/acts/controllers/native.py
@@ -17,8 +17,6 @@
 from acts.controllers.sl4a_lib.rpc_connection import RpcConnection
 import json
 import os
-import socket
-import threading
 
 HOST = os.environ.get('AP_HOST', None)
 PORT = os.environ.get('AP_PORT', 9999)
diff --git a/acts/framework/acts/controllers/packet_capture.py b/acts/framework/acts/controllers/packet_capture.py
index 1e6d96d..9a73682 100755
--- a/acts/framework/acts/controllers/packet_capture.py
+++ b/acts/framework/acts/controllers/packet_capture.py
@@ -28,7 +28,6 @@
 from acts.libs.proc.process import Process
 from acts import asserts
 
-import logging
 import os
 import threading
 import time
@@ -71,6 +70,7 @@
         pcap_fname: File name of the tcpdump output file
         pcap_file: File object for the tcpdump output file
     """
+
     def __init__(self, proc, pcap_fname, pcap_file):
         """Initialize object."""
         self.proc = proc
@@ -93,6 +93,7 @@
         pcap_properties: dict that specifies packet capture properties for a
             band.
     """
+
     def __init__(self, configs):
         """Initialize objects.
 
@@ -122,22 +123,25 @@
         Create mon0/mon1 for 2G/5G monitor mode and wlan2 for managed mode.
         """
         if mode == 'monitor':
-            self.ssh.run('ifconfig wlan%s down' % iface[-1], ignore_status=True)
+            self.ssh.run('ifconfig wlan%s down' % iface[-1],
+                         ignore_status=True)
         self.ssh.run('iw dev %s del' % iface, ignore_status=True)
-        self.ssh.run('iw phy%s interface add %s type %s'
-                     % (iface[-1], iface, mode), ignore_status=True)
+        self.ssh.run('iw phy%s interface add %s type %s' %
+                     (iface[-1], iface, mode),
+                     ignore_status=True)
         self.ssh.run('ip link set %s up' % iface, ignore_status=True)
         result = self.ssh.run('iw dev %s info' % iface, ignore_status=True)
         if result.stderr or iface not in result.stdout:
-            raise PacketCaptureError('Failed to configure interface %s' % iface)
+            raise PacketCaptureError('Failed to configure interface %s' %
+                                     iface)
 
     def _cleanup_interface(self, iface):
         """Clean up monitor mode interfaces."""
         self.ssh.run('iw dev %s del' % iface, ignore_status=True)
         result = self.ssh.run('iw dev %s info' % iface, ignore_status=True)
         if not result.stderr or 'No such device' not in result.stderr:
-            raise PacketCaptureError('Failed to cleanup monitor mode for %s'
-                                     % iface)
+            raise PacketCaptureError('Failed to cleanup monitor mode for %s' %
+                                     iface)
 
     def _parse_scan_results(self, scan_result):
         """Parses the scan dump output and returns list of dictionaries.
@@ -225,8 +229,8 @@
 
         iface = BAND_IFACE[band]
         if bandwidth == 20:
-            self.ssh.run('iw dev %s set channel %s' %
-                         (iface, channel), ignore_status=True)
+            self.ssh.run('iw dev %s set channel %s' % (iface, channel),
+                         ignore_status=True)
         else:
             center_freq = None
             for i, j in CENTER_CHANNEL_MAP[VHT_CHANNEL[bandwidth]]["channels"]:
@@ -235,9 +239,10 @@
                     break
             asserts.assert_true(center_freq,
                                 "No match channel in VHT channel list.")
-            self.ssh.run('iw dev %s set freq %s %s %s' %
-                (iface, FREQUENCY_MAP[channel],
-                bandwidth, center_freq), ignore_status=True)
+            self.ssh.run(
+                'iw dev %s set freq %s %s %s' %
+                (iface, FREQUENCY_MAP[channel], bandwidth, center_freq),
+                ignore_status=True)
 
         result = self.ssh.run('iw dev %s info' % iface, ignore_status=True)
         if result.stderr or 'channel %s' % channel not in result.stdout:
@@ -269,11 +274,13 @@
         pcap_file = open(pcap_fname, 'w+b')
 
         tcpdump_cmd = 'tcpdump -i %s -w - -U 2>/dev/null' % (BAND_IFACE[band])
-        cmd = formatter.SshFormatter().format_command(
-            tcpdump_cmd, None, self.ssh_settings, extra_flags={'-q': None})
+        cmd = formatter.SshFormatter().format_command(tcpdump_cmd,
+                                                      None,
+                                                      self.ssh_settings,
+                                                      extra_flags={'-q': None})
         pcap_proc = Process(cmd)
-        pcap_proc.set_on_output_callback(
-            lambda msg: pcap_file.write(msg), binary=True)
+        pcap_proc.set_on_output_callback(lambda msg: pcap_file.write(msg),
+                                         binary=True)
         pcap_proc.start()
 
         self.pcap_properties[band] = PcapProperties(pcap_proc, pcap_fname,
diff --git a/acts/framework/acts/controllers/pdu.py b/acts/framework/acts/controllers/pdu.py
index 6b2b75c..3bd900c 100644
--- a/acts/framework/acts/controllers/pdu.py
+++ b/acts/framework/acts/controllers/pdu.py
@@ -160,6 +160,7 @@
     pdu_lib/<brand>/<device_name>.py. PduDevice objects should not be
     instantiated by users directly.
     """
+
     def __init__(self, host, username, password):
         if type(self) is PduDevice:
             raise NotImplementedError(
@@ -215,4 +216,3 @@
 
 class PduError(Exception):
     """An exception for use within PduDevice implementations"""
-    pass
\ No newline at end of file
diff --git a/acts/framework/acts/controllers/pdu_lib/digital_loggers/webpowerswitch.py b/acts/framework/acts/controllers/pdu_lib/digital_loggers/webpowerswitch.py
index c7e8091..0631435 100644
--- a/acts/framework/acts/controllers/pdu_lib/digital_loggers/webpowerswitch.py
+++ b/acts/framework/acts/controllers/pdu_lib/digital_loggers/webpowerswitch.py
@@ -14,10 +14,19 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+from acts import signals
 from acts.controllers import pdu
 
-import dlipower
-import time
+# Create an optional dependency for dlipower since it has a transitive
+# dependency on beautifulsoup4. This library is difficult to maintain as a
+# third_party dependency in Fuchsia since it is hosted on launchpad.
+#
+# TODO(b/246999212): Explore alternatives to the dlipower package
+try:
+    import dlipower
+    HAS_IMPORT_DLIPOWER = True
+except ImportError:
+    HAS_IMPORT_DLIPOWER = False
 
 
 class PduDevice(pdu.PduDevice):
@@ -32,12 +41,19 @@
         - WebPowerSwitch II
         - Ethernet Power Controller III
     """
+
     def __init__(self, host, username, password):
         """
         Note: This may require allowing plaintext password sign in on the
         power switch, which can be configure in the device's control panel.
         """
         super(PduDevice, self).__init__(host, username, password)
+
+        if not HAS_IMPORT_DLIPOWER:
+            raise signals.ControllerError(
+                'Digital Loggers PDUs are not supported with current installed '
+                'packages; install the dlipower package to add support')
+
         self.power_switch = dlipower.PowerSwitch(hostname=host,
                                                  userid=username,
                                                  password=password)
diff --git a/acts/framework/acts/controllers/relay_device_controller.py b/acts/framework/acts/controllers/relay_device_controller.py
index 528e166..0f713ec 100644
--- a/acts/framework/acts/controllers/relay_device_controller.py
+++ b/acts/framework/acts/controllers/relay_device_controller.py
@@ -81,7 +81,6 @@
     """
     for device in relay_devices:
         device.clean_up()
-    pass
 
 
 def get_info(relay_devices):
diff --git a/acts/framework/acts/controllers/relay_lib/errors.py b/acts/framework/acts/controllers/relay_lib/errors.py
index 39108db..f6fd05a 100644
--- a/acts/framework/acts/controllers/relay_lib/errors.py
+++ b/acts/framework/acts/controllers/relay_lib/errors.py
@@ -18,9 +18,7 @@
 
 class RelayConfigError(signals.ControllerError):
     """An error found within the RelayRig config file."""
-    pass
 
 
 class RelayDeviceConnectionError(signals.ControllerError):
     """An error for being unable to connect to the device."""
-    pass
diff --git a/acts/framework/acts/controllers/relay_lib/relay_device.py b/acts/framework/acts/controllers/relay_lib/relay_device.py
index 657d568..bfd6d76 100644
--- a/acts/framework/acts/controllers/relay_lib/relay_device.py
+++ b/acts/framework/acts/controllers/relay_lib/relay_device.py
@@ -54,8 +54,6 @@
 
     def setup(self):
         """Sets up the relay device to be ready for commands."""
-        pass
 
     def clean_up(self):
         """Sets the relay device back to its inert state."""
-        pass
diff --git a/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500.py b/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500.py
index 755962e..eb54b55 100644
--- a/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500.py
+++ b/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500.py
@@ -26,9 +26,42 @@
 LTE_PSWITCHED_ON_RESP = 'ON'
 LTE_PSWITCHED_OFF_RESP = 'OFF'
 
+WCDMA_ATTACH_RESP = 'ATT'
+WCDMA_CESTABLISHED_RESP = 'CEST'
+WCDMA_PSWITCHED_ON_RESP = 'ON'
+
 STATE_CHANGE_TIMEOUT = 20
 
 
+class IPAddressType(Enum):
+    """ IP Address types"""
+    IPV4 = "IPV4"
+    IPV6 = "IPV6"
+    IPV4V6 = "IPV46"
+
+
+class SignallingState(Enum):
+    """Signalling states common to all RATs."""
+    ON = 'ON'
+    OFF = 'OFF'
+    ReadyForHandover = 'RFH'
+
+
+class SccActivationMode(Enum):
+    """Activation mode to use for SCCs."""
+    AUTO = 'AUTO'
+    MANUAL = 'MAN'
+    SEMI_AUTO = 'SEM'
+
+
+class SccState(Enum):
+    """Secondary component carrier states."""
+    ON = 'ON'
+    OFF = 'OFF'
+    RRC = 'RRC'
+    MAC = 'MAC'
+
+
 class LteState(Enum):
     """LTE ON and OFF"""
     LTE_ON = 'ON'
@@ -56,6 +89,32 @@
     BANDWIDTH_20MHz = 'B200'
 
 
+def BandwidthFromFloat(bw):
+    if bw == 20:
+        return LteBandwidth.BANDWIDTH_20MHz
+    elif bw == 15:
+        return LteBandwidth.BANDWIDTH_15MHz
+    elif bw == 10:
+        return LteBandwidth.BANDWIDTH_10MHz
+    elif bw == 5:
+        return LteBandwidth.BANDWIDTH_5MHz
+    elif bw == 3:
+        return LteBandwidth.BANDWIDTH_3MHz
+    elif bw == 1.4:
+        return LteBandwidth.BANDWIDTH_1MHz
+
+    raise ValueError('Bandwidth {} MHz is not valid for LTE'.format(bandwidth))
+
+
+class DrxMode(Enum):
+    """DRX Modes."""
+    DRXS = 'DRXS'
+    DRXL = 'DRXL'
+    USER_DEFINED = 'UDEF'
+    ON = 'ON'
+    OFF = 'OFF'
+
+
 class DuplexMode(Enum):
     """Duplex Modes"""
     FDD = 'FDD'
@@ -174,7 +233,6 @@
 
 
 class Cmw500(abstract_inst.SocketInstrument):
-
     def __init__(self, ip_addr, port):
         """Init method to setup variables for controllers.
 
@@ -219,6 +277,51 @@
         else:
             raise CmwError('Failed to turn {} LTE signalling.'.format(state))
 
+    def switch_scc_state(self, scc_index, state):
+        """ Changes the SCC to the requested state.
+
+        Args:
+            scc_index: the SCC number to modify.
+            state: an instance of SccState indicating which SCC state to set.
+        """
+        cmd = 'CALL:LTE:SIGN:SCC{}:ACTion {}'.format(scc_index, state.value)
+        self.send_and_recv(cmd)
+        self.wait_for_scc_state(scc_index, [state])
+
+    def wait_for_scc_state(self, scc_index, allowed, timeout=120):
+        """ Polls for a SCC to reach the requested state.
+
+        Args:
+            scc_index: an integer defining the scc number.
+            allowed: a list of SccStates defining the allowed states.
+            timeout: the maximum amount of time to wait for the requested state.
+        """
+        self.wait_for_response(
+            'FETCh:LTE:SIGN:SCC{}:STATe?'.format(scc_index),
+            [a.value for a in allowed],
+            timeout,
+        )
+
+    def wait_for_response(self, cmd, allowed, timeout=120):
+        """Polls a specific query command until an allowed response is returned.
+
+        Args:
+            cmd: the query command string.
+            allowed: a collection of allowed string responses.
+            timeout: the maximum amount of time to wait for an allowed response.
+        """
+        time_elapsed = 0
+        while time_elapsed < timeout:
+            response = self.send_and_recv(cmd)
+
+            if response in allowed:
+                return
+
+            time.sleep(1)
+            time_elapsed += 1
+
+        raise CmwError('Failed to wait for valid response.')
+
     def enable_packet_switching(self):
         """Enable packet switching in call box."""
         self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion CONNect')
@@ -342,6 +445,15 @@
         else:
             raise CmwError('Timeout before RRC state was {}.'.format(state))
 
+    def stop_all_signalling(self):
+        """Turns off all signaling applications, generators, or measurements."""
+        self.send_and_recv('SYSTem:SIGNaling:ALL:OFF')
+        self.wait_until_quiet()
+
+    def wait_until_quiet(self):
+        """Waits for all pending operations to stop on the callbox."""
+        self.send_and_recv('*OPC?')
+
     def reset(self):
         """System level reset"""
         self.send_and_recv('*RST; *OPC')
@@ -400,6 +512,27 @@
         self.send_and_recv(cmd)
 
     @property
+    def scc_activation_mode(self):
+        """Gets the activation mode to use for SCCs when establishing a
+        connection.
+        """
+        return self.send_and_recv('CONFigure:LTE:SIGN:SCC:AMODe?')
+
+    @scc_activation_mode.setter
+    def scc_activation_mode(self, activation_mode):
+        """Sets the activation mode to use with SCCs when establishing a
+        connection.
+
+        Args:
+            activation_mode: the scc activation mode to use.
+        """
+        if not isinstance(activation_mode, SccActivationMode):
+            raise ValueError('state should be the instance of RrcState.')
+
+        cmd = 'CONFigure:LTE:SIGN:SCC:AMODe {}'.format(activation_mode.value)
+        self.send_and_recv(cmd)
+
+    @property
     def dl_mac_padding(self):
         """Gets the state of mac padding."""
         return self.send_and_recv('CONFigure:LTE:SIGN:CONNection:DLPadding?')
@@ -429,6 +562,224 @@
         cmd = 'CONFigure:LTE:SIGN:CONNection:CTYPe {}'.format(ctype.value)
         self.send_and_recv(cmd)
 
+    @property
+    def drx_connected_mode(self):
+        """ Gets the Connected DRX LTE cell parameter
+
+        Args:
+            None
+
+        Returns:
+            DRX connected mode (ON, OFF, USER_DEFINED, DRX_S, DRX_L)
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ENABle?'
+        return self.send_and_recv(cmd)
+
+    @drx_connected_mode.setter
+    def drx_connected_mode(self, mode):
+        """  Sets the Connected DRX LTE cell parameter
+
+        Args:
+            mode: DRX mode
+
+        Returns:
+            None
+        """
+        if not isinstance(mode, DrxMode):
+            raise ValueError('state should be the instance of DrxMode.')
+
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ENABle {}'.format(mode.value)
+        self.send_and_recv(cmd)
+
+    @property
+    def drx_on_duration_timer(self):
+        """ Gets the amount of PDCCH subframes to wait for data after
+            waking up from a DRX cycle
+
+        Args:
+            None
+
+        Returns:
+            DRX mode duration timer
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ODTimer?'
+        return self.send_and_recv(cmd)
+
+    @drx_on_duration_timer.setter
+    def drx_on_duration_timer(self, time):
+        """ Sets the amount of PDCCH subframes to wait for data after
+            waking up from a DRX cycle
+
+        Args:
+            timer: Length of interval to wait for user data to be transmitted
+
+        Returns:
+            None
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ODTimer {}'.format(time)
+        self.send_and_recv(cmd)
+
+    @property
+    def drx_inactivity_timer(self):
+        """ Gets the number of PDCCH subframes to wait before entering DRX mode
+
+        Args:
+            None
+
+        Returns:
+            DRX mode inactivity timer
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ITIMer?'
+        return self.send_and_recv(cmd)
+
+    @drx_inactivity_timer.setter
+    def drx_inactivity_timer(self, time):
+        """ Sets the number of PDCCH subframes to wait before entering DRX mode
+
+        Args:
+            timer: Length of the interval to wait
+
+        Returns:
+            None
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:ITIMer {}'.format(time)
+        self.send_and_recv(cmd)
+
+    @property
+    def drx_retransmission_timer(self):
+        """ Gets the number of consecutive PDCCH subframes to wait
+        for retransmission
+
+        Args:
+            None
+
+        Returns:
+            Number of PDCCH subframes to wait for retransmission
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:RTIMer?'
+        self.send_and_recv(cmd)
+
+    @drx_retransmission_timer.setter
+    def drx_retransmission_timer(self, time):
+        """ Sets the number of consecutive PDCCH subframes to wait
+        for retransmission
+
+        Args:
+            time: Number of PDCCH subframes to wait
+            for retransmission
+
+        Returns:
+            None
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:RTIMer {}'.format(time)
+        self.send_and_recv(cmd)
+
+    @property
+    def drx_long_cycle(self):
+        """ Gets the amount of subframes representing a DRX long cycle
+
+        Args:
+            None
+
+        Returns:
+            The amount of subframes representing one long DRX cycle.
+            One cycle consists of DRX sleep + DRX on duration
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:LDCYcle?'
+        return self.send_and_recv(cmd)
+
+    @drx_long_cycle.setter
+    def drx_long_cycle(self, long_cycle):
+        """ Sets the amount of subframes representing a DRX long cycle
+
+        Args:
+            long_cycle: The amount of subframes representing one long DRX cycle.
+                One cycle consists of DRX sleep + DRX on duration
+
+        Returns:
+            None
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:LDCYcle {}'.format(
+            long_cycle)
+        self.send_and_recv(cmd)
+
+    @property
+    def drx_long_cycle_offset(self):
+        """ Gets the offset used to determine long cycle starting
+        subframe
+
+        Args:
+            None
+
+        Returns:
+            Long cycle offset
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:SOFFset?'
+        return self.send_and_recv(cmd)
+
+    @drx_long_cycle_offset.setter
+    def drx_long_cycle_offset(self, offset):
+        """ Sets the offset used to determine long cycle starting
+        subframe
+
+        Args:
+            offset: Number in range 0...(long cycle - 1)
+        """
+        cmd = 'CONFigure:LTE:SIGN:CONNection:CDRX:SOFFset {}'.format(offset)
+        self.send_and_recv(cmd)
+
+    @property
+    def apn(self):
+        """Gets the callbox network Access Point Name."""
+        cmd = 'CONFigure:LTE:SIGNaling:CONNection:APN?'
+        return self.send_and_recv(cmd)
+
+    @apn.setter
+    def apn(self, apn):
+        """Sets the callbox network Access Point Name.
+
+        Args:
+            apn: the APN name
+        """
+        cmd = 'CONFigure:LTE:SIGNaling:CONNection:APN {}'.format(apn)
+        self.send_and_recv(cmd)
+
+    @property
+    def ip_type(self):
+        """Gets the callbox network IP type."""
+        cmd = 'CONFigure:LTE:SIGNaling:CONNection:IPVersion?'
+        return self.send_and_recv(cmd)
+
+    @ip_type.setter
+    def ip_type(self, ip_type):
+        """ Configures the callbox network IP type.
+
+        Args:
+            ip_type: the network type to use.
+        """
+        if not isinstance(ip_type, IPAddressType):
+            raise ValueError('state should be the instance of IPAddressType.')
+
+        cmd = 'CONFigure:LTE:SIGNaling:CONNection:IPVersion {}'.format(
+            ip_type.value)
+        return self.send_and_recv(cmd)
+
+    @property
+    def mtu(self):
+        """Gets the callbox network Maximum Transmission Unit."""
+        cmd = 'CONFigure:DATA:CONTrol:MTU?'
+        return self.send_and_recv(cmd)
+
+    @mtu.setter
+    def mtu(self, mtu):
+        """Sets the callbox network Maximum Transmission Unit.
+
+        Args:
+            mtu: the MTU size.
+        """
+        cmd = 'CONFigure:DATA:CONTrol:MTU {}'.format(mtu)
+        self.send_and_recv(cmd)
+
     def get_base_station(self, bts_num=BtsNumber.BTS1):
         """Gets the base station object based on bts num. By default
         bts_num set to PCC
@@ -450,10 +801,31 @@
         """
         return LteMeasurement(self)
 
+    def set_sms(self, message):
+        """Sets the SMS message to be sent to the DUT.
+
+        Args:
+            message: the SMS message to send.
+        """
+        cmd = 'CONFigure:LTE:SIGN:SMS:OUTGoing:INTernal "{}"'.format(message)
+        self.send_and_recv(cmd)
+
+    def send_sms(self):
+        """Sends the currently set SMS message."""
+        self.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion SMS;*OPC?')
+        timeout = time.time() + STATE_CHANGE_TIMEOUT
+        while time.time() < timeout:
+            state = self.send_and_recv(
+                'SENSe:LTE:SIGN:SMS:OUTGoing:INFO:LMSent?')
+            if state == "SUCC":
+                return
+            time.sleep(1)
+
+        raise CmwError('Failed to send SMS message')
+
 
 class BaseStation(object):
     """Class to interact with different base stations"""
-
     def __init__(self, cmw, bts_num):
         if not isinstance(bts_num, BtsNumber):
             raise ValueError('bts_num should be an instance of BtsNumber.')
@@ -461,6 +833,12 @@
         self._cmw = cmw
 
     @property
+    def scc_state(self):
+        """Gets the scc state of cell."""
+        cmd = 'FETCh:LTE:SIGN:{}:STATe?'.format(self._bts)
+        return self._cmw.send_and_recv(cmd)
+
+    @property
     def duplex_mode(self):
         """Gets current duplex of cell."""
         cmd = 'CONFigure:LTE:SIGN:{}:DMODe?'.format(self._bts)
@@ -884,13 +1262,14 @@
             raise ValueError('dci_format should be the instance of DciFormat.')
 
         cmd = 'CONFigure:LTE:SIGN:CONNection:{}:DCIFormat {}'.format(
-            self._bts, dci_format)
+            self._bts, dci_format.value)
         self._cmw.send_and_recv(cmd)
 
     @property
     def dl_antenna(self):
         """Gets dl antenna count of cell."""
-        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:NENBantennas?'.format(self._bts)
+        cmd = 'CONFigure:LTE:SIGN:CONNection:{}:NENBantennas?'.format(
+            self._bts)
         return self._cmw.send_and_recv(cmd)
 
     @dl_antenna.setter
@@ -903,7 +1282,7 @@
         if not isinstance(num_antenna, MimoModes):
             raise ValueError('num_antenna should be an instance of MimoModes.')
         cmd = 'CONFigure:LTE:SIGN:CONNection:{}:NENBantennas {}'.format(
-            self._bts, num_antenna)
+            self._bts, num_antenna.value)
         self._cmw.send_and_recv(cmd)
 
     @property
@@ -924,6 +1303,13 @@
             self._bts, state.value)
         self._cmw.send_and_recv(cmd)
 
+    @property
+    def tpc_power_control(self):
+        """Gets the type of uplink power control used."""
+        cmd = 'CONFigure:LTE:SIGN:UL:{}:PUSCh:TPC:SET?'.format(self._bts)
+        return self._cmw.send_and_recv(cmd)
+
+    @tpc_power_control.setter
     def tpc_power_control(self, set_type):
         """Set and execute the Up Link Power Control via TPC.
 
@@ -957,166 +1343,114 @@
             self._bts, cltpower)
         self._cmw.send_and_recv(cmd)
 
-    @property
-    def drx_connected_mode(self):
-        """ Gets the Connected DRX LTE cell parameter
 
-        Args:
-            None
+class LteMeasurementState(Enum):
+    """Possible measurement states"""
+    OFF = 'OFF'
+    READY = 'RDY'
+    RUN = 'RUN'
 
-        Returns:
-            DRX connected mode (OFF, AUTO, MANUAL)
-        """
-        raise NotImplementedError()
 
-    @drx_connected_mode.setter
-    def drx_connected_mode(self, mode):
-        """  Sets the Connected DRX LTE cell parameter
-
-        Args:
-            mode: DRX Connected mode
-
-        Returns:
-            None
-        """
-        raise NotImplementedError()
-
-    @property
-    def drx_on_duration_timer(self):
-        """ Gets the amount of PDCCH subframes to wait for data after
-            waking up from a DRX cycle
-
-        Args:
-            None
-
-        Returns:
-            DRX mode duration timer
-        """
-        raise NotImplementedError()
-
-    @drx_on_duration_timer.setter
-    def drx_on_duration_timer(self, time):
-        """ Sets the amount of PDCCH subframes to wait for data after
-            waking up from a DRX cycle
-
-        Args:
-            timer: Length of interval to wait for user data to be transmitted
-
-        Returns:
-            None
-        """
-        raise NotImplementedError()
-
-    @property
-    def drx_inactivity_timer(self):
-        """ Gets the number of PDCCH subframes to wait before entering DRX mode
-
-        Args:
-            None
-
-        Returns:
-            DRX mode inactivity timer
-        """
-        raise NotImplementedError()
-
-    @drx_inactivity_timer.setter
-    def drx_inactivity_timer(self, time):
-        """ Sets the number of PDCCH subframes to wait before entering DRX mode
-
-        Args:
-            timer: Length of the interval to wait
-
-        Returns:
-            None
-        """
-        raise NotImplementedError()
-
-    @property
-    def drx_retransmission_timer(self):
-        """ Gets the number of consecutive PDCCH subframes to wait
-        for retransmission
-
-        Args:
-            None
-
-        Returns:
-            Number of PDCCH subframes to wait for retransmission
-        """
-        raise NotImplementedError()
-
-    @drx_retransmission_timer.setter
-    def drx_retransmission_timer(self, time):
-        """ Sets the number of consecutive PDCCH subframes to wait
-        for retransmission
-
-        Args:
-            time: Number of PDCCH subframes to wait
-            for retransmission
-
-        Returns:
-            None
-        """
-        raise NotImplementedError()
-
-    @property
-    def drx_long_cycle(self):
-        """ Gets the amount of subframes representing a DRX long cycle
-
-        Args:
-            None
-
-        Returns:
-            The amount of subframes representing one long DRX cycle.
-            One cycle consists of DRX sleep + DRX on duration
-        """
-        raise NotImplementedError()
-
-    @drx_long_cycle.setter
-    def drx_long_cycle(self, time):
-        """ Sets the amount of subframes representing a DRX long cycle
-
-        Args:
-            long_cycle: The amount of subframes representing one long DRX cycle.
-                One cycle consists of DRX sleep + DRX on duration
-
-        Returns:
-            None
-        """
-        raise NotImplementedError()
-
-    @property
-    def drx_long_cycle_offset(self):
-        """ Gets the offset used to determine long cycle starting
-        subframe
-
-        Args:
-            None
-
-        Returns:
-            Long cycle offset
-        """
-        raise NotImplementedError()
-
-    @drx_long_cycle_offset.setter
-    def drx_long_cycle_offset(self, offset):
-        """ Sets the offset used to determine long cycle starting
-        subframe
-
-        Args:
-            offset: Number in range 0...(long cycle - 1)
-        """
-        raise NotImplementedError()
-
+def _try_parse(s, fun):
+    try:
+        return fun(s)
+    except ValueError:
+        return None
 
 
 class LteMeasurement(object):
+    """ Class for measuring LTE performance """
+
+    INDEX_TX = 17
+    INDEX_TX_MIN = 17
+    INDEX_TX_MAX = 18
 
     def __init__(self, cmw):
         self._cmw = cmw
 
-    def intitilize_measurement(self):
-        """Initialize measurement modules."""
-        self._cmw.send_and_recv('INIT:LTE:MEAS:MEValuation')
+    @property
+    def sample_count(self):
+        """Gets the number of samples to use when calculating results."""
+        cmd = 'CONFigure:LTE:MEAS:MEValuation:SCOunt:MODulation?'
+        return self._cmw.send_and_recv(cmd)
+
+    @sample_count.setter
+    def sample_count(self, sample_count):
+        cmd = 'CONFigure:LTE:MEAS:MEValuation:SCOunt:MODulation {}'.format(
+            sample_count)
+        self._cmw.send_and_recv(cmd)
+
+    @property
+    def average(self):
+        """Gets the average values of the most recent measurement.
+        Invalid measurements are set to None.
+        """
+        values = self._cmw.send_and_recv(
+            'FETch:LTE:MEAS:MEValuation:MODulation:AVERage?').split(',')
+        for i in range(0, 2):
+            values[i] = _try_parse(values[i], int)
+        for i in range(2, len(values)):
+            values[i] = _try_parse(values[i], float)
+        return values
+
+    @property
+    def extrema(self):
+        """Gets the extrema values of the most recent measurement.
+        Invalid measurements are set to None.
+        """
+        values = self._cmw.send_and_recv(
+            'FETch:LTE:MEAS:MEValuation:MODulation:EXTReme?').split(',')
+        for i in range(0, 2):
+            values[i] = _try_parse(values[i], int)
+        for i in range(2, len(values)):
+            values[i] = _try_parse(values[i], float)
+        return values
+
+    @property
+    def stdev(self):
+        """Gets the standard deviation of the most recent measurement.
+        Invalid measurements are set to None.
+        """
+        values = self._cmw.send_and_recv(
+            'FETch:LTE:MEAS:MEValuation:MODulation:SDEV?').split(',')
+        for i in range(0, 2):
+            values[i] = _try_parse(values[i], int)
+        for i in range(2, len(values)):
+            values[i] = _try_parse(values[i], float)
+        return values
+
+    @property
+    def tx_average(self):
+        """Gets the measured average Tx power (in dBm)."""
+        value = self.average[self.INDEX_TX]
+        if value is None:
+            raise ValueError("LteMeasurement has no value for tx_average")
+        return value
+
+    @property
+    def tx_min(self):
+        """Gets the measured minimum Tx power (in dBm)."""
+        value = self.extrema[self.INDEX_TX_MIN]
+        if value is None:
+            raise ValueError("LteMeasurement has no value for tx_min")
+        return value
+
+    @property
+    def tx_max(self):
+        """Gets the measured maximum Tx power (in dBm)."""
+        value = self.extrema[self.INDEX_TX_MAX]
+        if value is None:
+            raise ValueError("LteMeasurement has no value for tx_max")
+        return value
+
+    @property
+    def tx_stdev(self):
+        """Gets the measured Tx power standard deviation (in dBm)."""
+        value = self.stdev[self.INDEX_TX]
+        if value is None:
+            raise ValueError(
+                "LteMeasurement has no value for standard_deviation")
+        return value
 
     @property
     def measurement_repetition(self):
@@ -1148,12 +1482,35 @@
         return self._cmw.send_and_recv(
             'FETCh:LTE:MEAS:MEValuation:PMONitor:AVERage?')
 
+    @property
+    def state(self):
+        """Gets the state of the measurement."""
+        cmd = 'FETCh:LTE:MEAS:MEValuation:STATe?'
+        return LteMeasurementState(self._cmw.send_and_recv(cmd))
+
+    def initialize_measurement(self):
+        """Initialize measurement modules."""
+        self._cmw.send_and_recv(
+            'CONF:LTE:MEAS:MEV:RES:ALL ON,ON,ON,ON,ON,ON,ON,ON,ON,ON,ON,ON')
+        self._cmw.send_and_recv('ROUTe:LTE:MEAS:SCENario:CSPath "LTE Sig1"')
+        self._cmw.send_and_recv('INIT:LTE:MEAS:MEValuation;*OPC?')
+        self._wait_for_state({LteMeasurementState.RUN})
+
+    def run_measurement(self):
+        """Runs a single Tx multievaluation measurement to completion."""
+        self.stop_measurement()
+        self.measurement_repetition = RepetitionMode.SINGLESHOT
+        self.initialize_measurement()
+        self._wait_for_state({LteMeasurementState.READY}, timeout=120)
+
     def stop_measurement(self):
         """Stops the on-going measurement.
         This function call does not free up resources allocated for
         measurement. Instead it moves from RUN to RDY state.
         """
         self._cmw.send_and_recv('STOP:LTE:MEAS:MEValuation')
+        self._wait_for_state(
+            {LteMeasurementState.OFF, LteMeasurementState.READY})
 
     def abort_measurement(self):
         """Aborts the measurement abruptly.
@@ -1161,6 +1518,24 @@
         measurement and all the results will be wiped off.
         """
         self._cmw.send_and_recv('ABORt:LTE:MEAS:MEValuation')
+        self._wait_for_state({LteMeasurementState.OFF})
+
+    def _wait_for_state(self, states, timeout=10):
+        """Polls the measurement state until it reaches an allowable state
+
+        Args:
+            states: the allowed states
+            timeout: the maximum amount time to wait (in seconds)
+        """
+        while timeout > 0:
+            if self.state in states:
+                return
+
+            time.sleep(1)
+            timeout -= 1
+
+        raise CmwError(
+            'Failed to wait for LTE measurement state: {}.'.format(states))
 
 
 class CmwError(Exception):
diff --git a/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_cellular_simulator.py b/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_cellular_simulator.py
index c036904..4ac414b 100644
--- a/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_cellular_simulator.py
+++ b/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_cellular_simulator.py
@@ -17,6 +17,7 @@
 
 from acts.controllers.rohdeschwarz_lib import cmw500
 from acts.controllers import cellular_simulator as cc
+from acts.controllers.rohdeschwarz_lib import cmw500_scenario_generator as cg
 from acts.controllers.cellular_lib import LteSimulation
 
 CMW_TM_MAPPING = {
@@ -39,6 +40,18 @@
     LteSimulation.MimoMode.MIMO_4x4: cmw500.MimoModes.MIMO4x4
 }
 
+MIMO_ANTENNA_COUNT = {
+    LteSimulation.MimoMode.MIMO_1x1: 1,
+    LteSimulation.MimoMode.MIMO_2x2: 2,
+    LteSimulation.MimoMode.MIMO_4x4: 4,
+}
+
+IP_ADDRESS_TYPE_MAPPING = {
+    LteSimulation.IPAddressType.IPV4: cmw500.IPAddressType.IPV4,
+    LteSimulation.IPAddressType.IPV6: cmw500.IPAddressType.IPV6,
+    LteSimulation.IPAddressType.IPV4V6: cmw500.IPAddressType.IPV4V6,
+}
+
 # get mcs vs tbsi map with 256-qam disabled(downlink)
 get_mcs_tbsi_map_dl = {
     cmw500.ModulationType.QPSK: {
@@ -189,16 +202,29 @@
     def setup_lte_scenario(self):
         """ Configures the equipment for an LTE simulation. """
         self.cmw.connection_type = cmw500.ConnectionType.DAU
-        self.bts = [self.cmw.get_base_station()]
+        # if bts has not yet been initialized
+        if not self.bts:
+            self.bts = [self.cmw.get_base_station()]
         self.cmw.switch_lte_signalling(cmw500.LteState.LTE_ON)
 
-    def set_band_combination(self, bands):
-        """ Prepares the test equipment for the indicated band combination.
+    def set_band_combination(self, bands, mimo_modes):
+        """ Prepares the test equipment for the indicated band/mimo combination.
 
         Args:
             bands: a list of bands represented as ints or strings
+            mimo_modes: a list of LteSimulation.MimoMode to use for each carrier
         """
         self.num_carriers = len(bands)
+        self.bts = [self.cmw.get_base_station(cmw500.BtsNumber.BTS1)]
+        for i in range(1, len(bands)):
+            self.bts.append(
+                self.cmw.get_base_station(cmw500.BtsNumber('SCC{}'.format(i))))
+
+        antennas = [MIMO_ANTENNA_COUNT[m] for m in mimo_modes]
+        self.set_scenario(bands, antennas)
+
+    def get_band_combination(self):
+        return [int(bts.band.strip('OB')) for bts in self.bts]
 
     def set_lte_rrc_state_change_timer(self, enabled, time=10):
         """ Configures the LTE RRC state change timer.
@@ -252,9 +278,9 @@
             self.log.warning('Open loop supports-50dBm to 23 dBm. '
                              'Setting it to max power 23 dBm')
             input_power = 23
+        bts.tpc_closed_loop_target_power = input_power
         bts.uplink_power_control = input_power
         bts.tpc_power_control = cmw500.TpcPowerControl.CLOSED_LOOP
-        bts.tpc_closed_loop_target_power = input_power
 
     def set_output_power(self, bts_index, output_power):
         """ Sets the output power for the indicated base station.
@@ -335,19 +361,66 @@
         bts = self.bts[bts_index]
         mimo_mode = CMW_MIMO_MAPPING[mimo_mode]
         if mimo_mode == cmw500.MimoModes.MIMO1x1:
-            self.cmw.configure_mimo_settings(cmw500.MimoScenario.SCEN1x1)
+            self.set_bts_antenna(bts_index, 1)
             bts.dl_antenna = cmw500.MimoModes.MIMO1x1
 
         elif mimo_mode == cmw500.MimoModes.MIMO2x2:
-            self.cmw.configure_mimo_settings(cmw500.MimoScenario.SCEN2x2)
+            self.set_bts_antenna(bts_index, 2)
             bts.dl_antenna = cmw500.MimoModes.MIMO2x2
+            # set default transmission mode and DCI for this scenario
+            bts.transmode = cmw500.TransmissionModes.TM3
+            bts.dci_format = cmw500.DciFormat.D2A
 
         elif mimo_mode == cmw500.MimoModes.MIMO4x4:
-            self.cmw.configure_mimo_settings(cmw500.MimoScenario.SCEN4x4)
+            self.set_bts_antenna(bts_index, 4)
             bts.dl_antenna = cmw500.MimoModes.MIMO4x4
         else:
             raise RuntimeError('The requested MIMO mode is not supported.')
 
+    def set_bts_antenna(self, bts_index, antenna_count):
+        """ Sets the mimo mode for a particular component carrier.
+
+        Args:
+            bts_index: index of the base station to configure.
+            antenna_count: number of antennas to use for the component carrier
+                either (1, 2, 4)
+        """
+        antennas = self.get_antenna_combination()
+        antennas[bts_index] = antenna_count
+        bands = self.get_band_combination()
+        self.set_scenario(bands, antennas)
+
+    def get_antenna_combination(self):
+        """ Gets the current antenna configuration.
+
+        Returns:
+            antenna_count: a list containing the number of antennas to use for
+                each bts/CC.
+        """
+        cmd = 'ROUTe:LTE:SIGN:SCENario?'
+        scenario_name = self.cmw.send_and_recv(cmd)
+        return cg.get_antennas(scenario_name)
+
+    def set_scenario(self, bands, antennas):
+        """ Sets the MIMO scenario on the CMW.
+
+        Args:
+            bands: a list defining the bands to use for each CC.
+            antennas: a list of integers defining the number of antennas to use
+                for each bts/CC.
+        """
+        self.log.debug('Setting scenario: bands: {}, antennas: {}'.format(
+            bands, antennas))
+        scenario = cg.get_scenario(bands, antennas)
+        cmd = 'ROUTe:LTE:SIGN:SCENario:{}:FLEXible {}'.format(
+            scenario.name, scenario.routing)
+        self.cmw.send_and_recv(cmd)
+        self.cmw.scc_activation_mode = cmw500.SccActivationMode.AUTO
+
+        # apply requested bands now
+        for i, band in enumerate(bands):
+            self.set_band(i, str(band))
+
     def set_transmission_mode(self, bts_index, tmode):
         """ Sets the transmission mode for the indicated base station.
 
@@ -360,18 +433,15 @@
         tmode = CMW_TM_MAPPING[tmode]
 
         if (tmode in [
-            cmw500.TransmissionModes.TM1,
-            cmw500.TransmissionModes.TM7
+                cmw500.TransmissionModes.TM1, cmw500.TransmissionModes.TM7
         ] and bts.dl_antenna == cmw500.MimoModes.MIMO1x1.value):
             bts.transmode = tmode
-        elif (tmode.value in cmw500.TransmissionModes.__members__ and
-              bts.dl_antenna == cmw500.MimoModes.MIMO2x2.value):
+        elif (tmode.value in cmw500.TransmissionModes.__members__
+              and bts.dl_antenna == cmw500.MimoModes.MIMO2x2.value):
             bts.transmode = tmode
         elif (tmode in [
-            cmw500.TransmissionModes.TM2,
-            cmw500.TransmissionModes.TM3,
-            cmw500.TransmissionModes.TM4,
-            cmw500.TransmissionModes.TM9
+                cmw500.TransmissionModes.TM2, cmw500.TransmissionModes.TM3,
+                cmw500.TransmissionModes.TM4, cmw500.TransmissionModes.TM9
         ] and bts.dl_antenna == cmw500.MimoModes.MIMO4x4.value):
             bts.transmode = tmode
 
@@ -379,8 +449,13 @@
             raise ValueError('Transmission modes should support the current '
                              'mimo mode')
 
-    def set_scheduling_mode(self, bts_index, scheduling, mcs_dl=None,
-                            mcs_ul=None, nrb_dl=None, nrb_ul=None):
+    def set_scheduling_mode(self,
+                            bts_index,
+                            scheduling,
+                            mcs_dl=None,
+                            mcs_ul=None,
+                            nrb_dl=None,
+                            nrb_ul=None):
         """ Sets the scheduling mode for the indicated base station.
 
         Args:
@@ -474,7 +549,6 @@
             mac_padding: the new MAC padding setting
         """
         # TODO (b/143918664): CMW500 doesn't have an equivalent setting.
-        pass
 
     def set_cfi(self, bts_index, cfi):
         """ Sets the Channel Format Indicator for the indicated base station.
@@ -508,6 +582,95 @@
         self.log.error('Configuring the PHICH resource setting is not yet '
                        'implemented in the CMW500 controller.')
 
+    def set_drx_connected_mode(self, bts_index, active):
+        """ Sets the time interval to wait before entering DRX mode
+
+        Args:
+            bts_index: the base station number
+            active: Boolean indicating whether cDRX mode
+                is active
+        """
+        self.cmw.drx_connected_mode = (cmw500.DrxMode.USER_DEFINED
+                                       if active else cmw500.DrxMode.OFF)
+
+    def set_drx_on_duration_timer(self, bts_index, timer):
+        """ Sets the amount of PDCCH subframes to wait for data after
+            waking up from a DRX cycle
+
+        Args:
+            bts_index: the base station number
+            timer: Number of PDCCH subframes to wait and check for user data
+                after waking from the DRX cycle
+        """
+        timer = 'PSF{}'.format(timer)
+        self.cmw.drx_on_duration_timer = timer
+
+    def set_drx_inactivity_timer(self, bts_index, timer):
+        """ Sets the number of PDCCH subframes to wait before entering DRX mode
+
+        Args:
+            bts_index: the base station number
+            timer: The amount of time to wait before entering DRX mode
+        """
+        timer = 'PSF{}'.format(timer)
+        self.cmw.drx_inactivity_timer = timer
+
+    def set_drx_retransmission_timer(self, bts_index, timer):
+        """ Sets the number of consecutive PDCCH subframes to wait
+        for retransmission
+
+        Args:
+            bts_index: the base station number
+            timer: Number of PDCCH subframes to remain active
+        """
+        timer = 'PSF{}'.format(timer)
+        self.cmw.drx_retransmission_timer = timer
+
+    def set_drx_long_cycle(self, bts_index, cycle):
+        """ Sets the amount of subframes representing a DRX long cycle.
+
+        Args:
+            bts_index: the base station number
+            cycle: The amount of subframes representing one long DRX cycle.
+                One cycle consists of DRX sleep + DRX on duration
+        """
+        cycle = 'SF{}'.format(cycle)
+        self.cmw.drx_long_cycle = cycle
+
+    def set_drx_long_cycle_offset(self, bts_index, offset):
+        """ Sets the offset used to determine the subframe number
+        to begin the long drx cycle
+
+        Args:
+            bts_index: the base station number
+            offset: Number in range 0 to (long cycle - 1)
+        """
+        self.cmw.drx_long_cycle_offset = offset
+
+    def set_apn(self, apn):
+        """ Configures the callbox network Access Point Name.
+
+        Args:
+            apn: the APN name
+        """
+        self.cmw.apn = apn
+
+    def set_ip_type(self, ip_type):
+        """ Configures the callbox network IP type.
+
+        Args:
+            ip_type: the network type to use.
+        """
+        self.cmw.ip_type = IP_ADDRESS_TYPE_MAPPING[ip_type]
+
+    def set_mtu(self, mtu):
+        """ Configures the callbox network Maximum Transmission Unit.
+
+        Args:
+            mtu: the MTU size.
+        """
+        self.cmw.mtu = mtu
+
     def lte_attach_secondary_carriers(self, ue_capability_enquiry):
         """ Activates the secondary carriers for CA. Requires the DUT to be
         attached to the primary carrier first.
@@ -516,7 +679,26 @@
             ue_capability_enquiry: UE capability enquiry message to be sent to
         the UE before starting carrier aggregation.
         """
-        raise NotImplementedError()
+        for i in range(1, len(self.bts)):
+            bts = self.bts[i]
+            if bts.scc_state == cmw500.SccState.OFF.value:
+                if (self.cmw.scc_activation_mode ==
+                        cmw500.SccActivationMode.MANUAL.value):
+                    self.cmw.switch_scc_state(i, cmw500.SccState.ON)
+                # SEMI_AUTO -> directly to MAC activation
+                if (self.cmw.scc_activation_mode ==
+                        cmw500.SccActivationMode.SEMI_AUTO.value):
+                    self.cmw.switch_scc_state(i, cmw500.SccState.MAC)
+
+            # no need to do anything for auto
+            if self.cmw.scc_activation_mode != cmw500.SccActivationMode.AUTO.value:
+                if bts.scc_state == cmw500.SccState.ON.value:
+                    self.cmw.switch_scc_state(i, cmw500.SccState.RRC)
+
+                if bts.scc_state == cmw500.SccState.RRC.value:
+                    self.cmw.switch_scc_state(i, cmw500.SccState.MAC)
+
+            self.cmw.wait_for_scc_state(i, [cmw500.SccState.MAC])
 
     def wait_until_attached(self, timeout=120):
         """ Waits until the DUT is attached to the primary carrier.
@@ -560,6 +742,15 @@
                                             'Idle state before '
                                             'the timeout period ended.')
 
+    def wait_until_quiet(self, timeout=120):
+        """Waits for all pending operations to finish on the simulator.
+
+        Args:
+            timeout: after this amount of time the method will raise a
+                CellularSimulatorError exception. Default is 120 seconds.
+        """
+        self.cmw.send_and_recv('*OPC?')
+
     def detach(self):
         """ Turns off all the base stations so the DUT loose connection."""
         self.cmw.detach()
@@ -567,7 +758,8 @@
     def stop(self):
         """ Stops current simulation. After calling this method, the simulator
         will need to be set up again. """
-        raise NotImplementedError()
+        self.detach()
+        self.cmw.switch_lte_signalling(cmw500.LteState.LTE_OFF)
 
     def start_data_traffic(self):
         """ Starts transmitting data from the instrument to the DUT. """
@@ -576,3 +768,12 @@
     def stop_data_traffic(self):
         """ Stops transmitting data from the instrument to the DUT. """
         raise NotImplementedError()
+
+    def send_sms(self, message):
+        """ Sends an SMS message to the DUT.
+
+        Args:
+            message: the SMS message to send.
+        """
+        self.cmw.set_sms(message)
+        self.cmw.send_sms()
diff --git a/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_handover_simulator.py b/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_handover_simulator.py
new file mode 100644
index 0000000..be9abd5
--- /dev/null
+++ b/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_handover_simulator.py
@@ -0,0 +1,392 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""Provides cmw500 handover functionality."""
+
+from enum import Enum
+
+from acts.controllers import handover_simulator as hs
+from acts.controllers.rohdeschwarz_lib import cmw500
+
+
+class HandoverMode(Enum):
+    """Supported handover modes."""
+    Redirection = "RED"
+    MTCSFallback = "MTCS"
+    Handover = "HAND"
+
+
+class Cmw500HandoverSimulator(hs.AbstractHandoverSimulator):
+    """Provides methods for performing inter/intra-RAT handovers."""
+
+    def __init__(self, cmw):
+        """Init method to setup handover controller.
+
+        Args:
+            cmw: the CMW500 instrument.
+        """
+        self.cmw = cmw
+        self._lte = Cmw500LteHandoverManager(self.cmw)
+        self._wcdma = Cmw500WcdmaHandoverManager(self.cmw)
+
+    def lte_handover(self, band, channel, bandwidth, source_technology):
+        """Performs a handover to LTE.
+
+        Args:
+            band: the band of the handover destination.
+            channel: the downlink channel of the handover destination.
+            bandwidth: the downlink bandwidth of the handover destination.
+            source_technology: the source handover technology.
+        """
+        mode = cmw500.DuplexMode.TDD if 33 <= int(
+            band) <= 46 else cmw500.DuplexMode.FDD
+        band = 'OB{}'.format(band)
+        bandwidth = cmw500.BandwidthFromFloat(bandwidth)
+
+        source = self._get_handover_manager(source_technology)
+        self._prepare_handover(source, self._lte)
+        self._lte.configure_incoming_handover_lte(mode, band, channel,
+                                                  bandwidth)
+        self._perform_handover(source, self._lte)
+
+    def wcdma_handover(self, band, channel, source_technology):
+        """Performs a handover to WCDMA.
+
+        Args:
+            band: the band of the handover destination.
+            channel: the downlink channel of the handover destination.
+            source_technology: the source handover technology.
+        """
+        band = 'OB{}'.format(band)
+
+        source = self._get_handover_manager(source_technology)
+        self._prepare_handover(source, self._wcdma)
+        self._wcdma.configure_incoming_handover_wcdma(band, channel)
+        self._perform_handover(source, self._wcdma)
+
+    def _prepare_handover(self, source, destination):
+        """Initializes the source and destination signalling applications for a handover.
+
+        Args:
+            source: the source handover manager.
+            destination: the destination handover manager.
+        """
+        if not source.is_attached:
+            raise hs.HandoverSimulatorError(
+                "Unable to perform handover, source signalling application is not attached."
+            )
+
+        source.handover_destination = destination.application_name
+        if source.is_internal_handover:
+            destination.wait_for_signalling_state([
+                cmw500.SignallingState.ReadyForHandover.value,
+                cmw500.SignallingState.ON.value
+            ])
+            source.handover_mode = HandoverMode.Redirection
+        else:
+            destination.wait_for_signalling_state(
+                [cmw500.SignallingState.ReadyForHandover.value])
+            source.handover_mode = HandoverMode.Handover
+
+    def _perform_handover(self, source, destination):
+        """Performs the handover and wait for completion.
+
+        Args:
+            source: the source handover manager.
+            destination: the destination handover manager.
+        """
+        source.initiate_handover()
+        destination.wait_for_handover()
+        if not source.is_attached:
+            source.stop_signalling()
+
+        self.cmw.wait_until_quiet()
+
+    def _get_handover_manager(self, technology):
+        """Gets the handover manager for the specified technology.
+
+        Args:
+            technology: the handover source/destination technology.
+
+        Returns:
+            the manager for the specified technology.
+        """
+        if technology == hs.CellularTechnology.LTE:
+            return self._lte
+        if technology == hs.CellularTechnology.WCDMA:
+            return self._wcdma
+        raise hs.HandoverSimulatorError(
+            'Unsupported handover destination type {}.'.format(technology))
+
+
+class Cmw500HandoverManagerBase():
+    """Provides common CMW500 functionality for conducting handovers."""
+
+    def __init__(self, cmw, technology):
+        """Initializes handover controller.
+
+        Args:
+            cmw: the CMW500 instrument.
+            technology: the cellular technology to use.
+        """
+        self.cmw = cmw
+        self.tech = technology.value
+
+    @property
+    def application_name(self):
+        """Gets the name of the signalling application to be used in handovers."""
+        return '{} Sig1'.format(self.tech)
+
+    @property
+    def handover_destination(self):
+        """Gets the handover destination application."""
+        cmd = 'PREPare:{}:SIGN:HANDover:DESTination?'.format(self.tech)
+        return self.cmw.send_and_recv(cmd).strip('"\'')
+
+    @property
+    def is_internal_handover(self):
+        """Returns true if the handover is within the same signalling application."""
+        return self.handover_destination == self.application_name
+
+    @handover_destination.setter
+    def handover_destination(self, destination):
+        """Sets the handover destination application."""
+        cmd = 'PREPare:{}:SIGN:HANDover:DESTination "{}"'.format(
+            self.tech, destination)
+        self.cmw.send_and_recv(cmd)
+
+    @property
+    def handover_mode(self):
+        """Gets the handover mechanism to use."""
+        cmd = 'PREPare:{}:SIGN:HANDover:MMODe?'.format(self.tech)
+        return self.cmw.send_and_recv(cmd)
+
+    @handover_mode.setter
+    def handover_mode(self, mode):
+        """Sets the handover mechanism to use."""
+        if not isinstance(mode, HandoverMode):
+            raise ValueError('mode should be the instance of HandoverMode.')
+        self.cmw.send_and_recv('PREPare:{}:SIGN:HANDover:MMODe {}'.format(
+            self.tech, mode.value))
+
+    @property
+    def is_attached(self):
+        """Returns True if the current technology pswitched state is attached."""
+        cmd = 'FETCh:{}:SIGN:PSWitched:STATe?'.format(self.tech)
+        return self.cmw.send_and_recv(cmd) in self.get_attached_states()
+
+    def stop_signalling(self):
+        """Stops the current signalling application."""
+        cmd = 'SOURce:{}:SIGN:CELL:STATe {}'.format(
+            self.tech, cmw500.SignallingState.OFF.value)
+        self.cmw.send_and_recv(cmd)
+        self.wait_for_signalling_state([cmw500.SignallingState.OFF.value])
+
+    def wait_for_signalling_state(self, allowed, timeout=30):
+        """Polls the signalling state until it reaches an allowable state.
+
+        Args:
+            allowed: a list of strings defining allowed signalling state responses.
+            timeout: timeout for valid state to be reached.
+
+        Raises:
+            CmwError on time out.
+        """
+        allowed = set(['{},ADJ'.format(state) for state in allowed])
+        cmd = 'SOURce:{}:SIGN:CELL:STATe:ALL?'.format(self.tech)
+        self.cmw.wait_for_response(cmd, allowed, timeout=timeout)
+
+    def wait_for_pswitched_state(self, allowed, timeout=120):
+        """Polls the pswitched state until it reaches an allowable state.
+
+        Args:
+            allowed: a list of strings defining valid pswitched state responses.
+            timeout: timeout for valid state to be reached.
+
+        Raises:
+            CmwError on time out.
+        """
+        cmd = 'FETCh:{}:SIGN:PSWitched:STATe?'.format(self.tech)
+        self.cmw.wait_for_response(cmd, allowed, timeout=timeout)
+
+    def get_attached_states(self):
+        """Gets a collection of valid responses when the application is attached.
+
+        Returns:
+            states: the list of valid attached states.
+        """
+        raise NotImplementedError()
+
+    def initiate_handover(self):
+        """Initiates an outgoing handover."""
+        raise NotImplementedError()
+
+    def wait_for_handover(self):
+        """Waits for an incoming handover to be completed."""
+        raise NotImplementedError()
+
+
+class Cmw500LteHandoverManager(Cmw500HandoverManagerBase):
+    """Provides LTE-specific handover methods."""
+
+    ATTACHED_STATES = [cmw500.LTE_ATTACH_RESP]
+
+    def __init__(self, cmw):
+        """Init method to setup handover controller.
+
+        Args:
+            cmw: the CMW500 instrument.
+        """
+        super().__init__(cmw, hs.CellularTechnology.LTE)
+
+    def configure_incoming_handover_lte(self, mode, band, channel, bandwidth):
+        """Prepares the LTE simulator for an incoming handover.
+
+        Args:
+            mode: the duplexing mode of the handover destination.
+            band: the band of the handover destination.
+            channel: the downlink channel of the handover destination.
+            bandwidth: the duplexing mode of the handover destination.
+        """
+        if self.is_attached:
+            self.configure_handover(mode, band, channel, bandwidth)
+        else:
+            bts = self.cmw.get_base_station()
+            bts.duplex_mode = mode
+            bts.band = band
+            bts.dl_channel = channel
+            bts.bandwidth = bandwidth
+
+    def initiate_handover(self):
+        """Initiates an outgoing handover."""
+        self.cmw.send_and_recv('CALL:LTE:SIGN:PSWitched:ACTion HANDover')
+
+    def wait_for_handover(self):
+        """Waits for an incoming handover to be completed."""
+        self.cmw.wait_for_attached_state()
+
+    def configure_handover(self, mode, band, channel, bandwidth, emit='NS01'):
+        """Configures the handover destination.
+
+        Args:
+            mode: the duplexing mode of the handover destination.
+            band: the band of the handover destination.
+            channel: the downlink channel of the handover destination.
+            bandwidth: the downlink bandwidth of the handover destination.
+            emit: an additional optional spectrum emissions requirement.
+        """
+        if not isinstance(bandwidth, cmw500.LteBandwidth):
+            raise ValueError('bandwidth should be an instance of '
+                             'LteBandwidth.')
+        if not isinstance(mode, cmw500.DuplexMode):
+            raise ValueError('mode should be an instance of ' 'DuplexMode.')
+        self.cmw.send_and_recv(
+            'PREPare:LTE:SIGN:HANDover:ENHanced {}, {}, {}, {}, {}'.format(
+                mode.value, band, channel, bandwidth.value, emit))
+
+    def get_attached_states(self):
+        """Gets a collection of valid attached states.
+
+        Returns:
+            states: the list of valid attached states.
+        """
+        return self.ATTACHED_STATES
+
+
+class Cmw500WcdmaHandoverManager(Cmw500HandoverManagerBase):
+    """Provides WCDMA-specific handover methods."""
+
+    ATTACHED_STATES = set(
+        [cmw500.WCDMA_ATTACH_RESP, cmw500.WCDMA_CESTABLISHED_RESP])
+
+    def __init__(self, cmw):
+        """Init method to setup handover controller.
+
+        Args:
+            cmw: the CMW500 instrument.
+        """
+        super().__init__(cmw, hs.CellularTechnology.WCDMA)
+        self._stored_band = self._band
+        self._stored_channel = self._dl_channel
+
+    @property
+    def _band(self):
+        """Sets the signalling application band."""
+        self.cmw.send_and_recv('CONFigure:WCDMa:SIGN:CARRier:BAND?')
+
+    @_band.setter
+    def _band(self, band):
+        """Sets the signalling application band.
+
+        Args:
+            band: the band of the signalling application.
+        """
+        cmd = 'CONFigure:WCDMa:SIGN:CARRier:BAND {}'.format(band)
+        self.cmw.send_and_recv(cmd)
+
+    @property
+    def _dl_channel(self):
+        """Gets the signalling application dl channel."""
+        cmd = 'CONFigure:WCDMa:SIGN:RFSettings:CARRier:CHANnel:DL?'
+        self.cmw.send_and_recv(cmd)
+
+    @_dl_channel.setter
+    def _dl_channel(self, channel):
+        """Sets the signalling application dl channel.
+
+        Args:
+            channel: the channel of the signalling application.
+        """
+        cmd = 'CONFigure:WCDMa:SIGN:RFSettings:CARRier:CHANnel:DL {}'.format(
+            channel)
+        self.cmw.send_and_recv(cmd)
+
+    def configure_incoming_handover_wcdma(self, band, channel):
+        """Prepares the WCDMA simulator for an incoming handover.
+
+        Args:
+            band: the band of the handover destination.
+            channel: the downlink channel of the handover destination.
+        """
+        # WCDMA has no dedicated configuration for handovers within the same
+        # signalling application instead, store the values for later and
+        # apply them when initiate_handover is called.
+        if not self.is_attached:
+            self._band = band
+            self._dl_channel = channel
+
+        self._stored_band = band
+        self._stored_channel = channel
+
+    def initiate_handover(self):
+        """Initiates an outgoing handover."""
+        if self.is_internal_handover:
+            self._dl_channel = self._stored_channel
+            self._band = self._stored_band
+        else:
+            self.cmw.send_and_recv('CALL:WCDMA:SIGN:PSWitched:ACTion HANDover')
+
+    def wait_for_handover(self):
+        """Waits for an incoming handover to be completed."""
+        self.wait_for_pswitched_state(
+            [cmw500.WCDMA_ATTACH_RESP, cmw500.WCDMA_CESTABLISHED_RESP])
+
+    def get_attached_states(self):
+        """Gets a collection of valid attached states.
+
+        Returns:
+            states: the list of valid attached states.
+        """
+        return self.ATTACHED_STATES
diff --git a/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_scenario_generator.py b/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_scenario_generator.py
new file mode 100644
index 0000000..5c3aaa5
--- /dev/null
+++ b/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_scenario_generator.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2023 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#           http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""Provides utilities for generating CMW500 CA scenarios based on band/mimo combinations.
+
+NOTE: Currently does not support:
+    1. Scenarios requiring multiple CMW500s
+    2. SUA coprocessing
+    3. Fading scenarios
+"""
+
+from collections import defaultdict
+from collections import namedtuple
+
+# Maps CMW scenario names to antenna (MIMO) configuration.
+SCENARIO_NAME_MAPPING = {
+    # 1CC
+    "SCEL": (1, ),
+    "TRO": (2, ),
+    "AD": (4, ),
+    # 2CC
+    "CATR": (1, 1),
+    "CAFR": (2, 2),
+    "BF": (4, 2),
+    "BFSM4": (2, 4),
+    "BH": (4, 4),
+    # 3CC
+    "CC": (1, 1, 1),
+    "CF": (2, 2, 2),
+    "CCMP": (2, 1, 1),
+    "CCMS1": (1, 2, 1),
+    # 4CC
+    "DD": (1, 1, 1, 1),
+    "DH": (2, 2, 2, 2),
+    # 5CC - 8CC require multiple CMWs
+}
+
+# Maps antenna combinations to CMW scenario name.
+SCENARIO_ANTENNA_MAPPING = {v: k for k, v in SCENARIO_NAME_MAPPING.items()}
+
+
+def get_scenario(bands, antennas):
+    """Gets a compatible scenario for the given band/antenna combination.
+
+    Args:
+        bands: a list defining the bands to use for each CC.
+        antennas: a list of integers defining the number of antennas to use for each CC.
+
+    Returns:
+        CMW500Scenario: the generated scenario.
+
+    Raises:
+        ValueError: if there is no scenario available for the given antenna/band combination.
+    """
+    antennas = tuple(antennas)
+    if not antennas in SCENARIO_ANTENNA_MAPPING:
+        raise ValueError(
+            "No CMW scenario matches antenna combination: {}".format(antennas))
+
+    generator = CMW500ScenarioGenerator()
+    port_configs = [generator.get_next(b, a) for b, a in zip(bands, antennas)]
+
+    scenario_name = SCENARIO_ANTENNA_MAPPING[antennas]
+    return CMW500Scenario(scenario_name, port_configs)
+
+
+def get_antennas(name):
+    """Gets the antenna combination mimo corresponding to a scenario name.
+
+    Args:
+        name: a string defining the scenario name.
+
+    Returns:
+        antennas: a list of integers defining the number of antennas for each CC.
+
+    Raises:
+        ValueError: if the scenario name is unknown.
+    """
+    if not name in SCENARIO_NAME_MAPPING:
+        raise ValueError("Unknown scenario name: {}".format(name))
+
+    return list(SCENARIO_NAME_MAPPING[name])
+
+
+class CMW500Scenario(object):
+    """A routed scenario in a CMW500."""
+
+    def __init__(self, name, configs):
+        """Initialize a CMW500 scenario from a name and PortConfiguration list.
+
+        Args:
+            name: a string defining the CMW500 scenario name.
+            configs: list(list(PortConfigurations)) defining the configurations for each CC.
+        """
+        self.name = name
+        self.configs = configs
+
+    @property
+    def routing(self):
+        """Gets the CMW routing text for the scenario.
+
+        Returns:
+            routing: a string defining the routing text for the CMW scenario command.
+        """
+        routing = []
+        i = 1
+        for carrier_config in self.configs:
+            routing.append("SUA{}".format(i))
+            # Assume PCC antenna & uplink are always on port 1
+            if i == 1:
+                routing.append("RF1C")
+                routing.append("RX1")
+            for config in carrier_config:
+                routing.append("RF{}C".format(config.port_id))
+                routing.append("TX{}".format(config.converter_id))
+            i += 1
+        return ",".join(routing)
+
+
+# A port/converter combination for a single callbox port in a component carrier.
+PortConfiguration = namedtuple("PortConfiguration", "port_id converter_id")
+
+
+class CMW500ScenarioGenerator(object):
+    """Class that is responsible for generating port/converter configurations for cmw500.
+
+    Generator prioritizes using the fewest total number of antenna ports it can.
+
+    Generation rules:
+        - There are 4 'converters'
+        - Each converter can be used up to a maximum of twice (if both CCs use the same band)
+        - Each converter has 2 potental antenna ports that it can be used with
+    """
+
+    # Maps converters to possible antenna ports.
+    PORT_MAPPING = {1: (1, 2), 2: (3, 4), 3: (1, 2), 4: (3, 4)}
+
+    # Maps antennas to possible converters.
+    CONVERTER_MAPPING = {1: (1, 3), 2: (1, 3), 3: (2, 4), 4: (2, 4)}
+
+    def __init__(self):
+        self.used_once = defaultdict(list)
+        self.free_converters = set([1, 2, 3, 4])
+
+    def get_next(self, band, antenna_count):
+        """Generates a routing configuration for the next component carrier in the sequence.
+
+        Returns:
+            configs a list of PortConfigurations defining the configuration to use for each port
+
+        Raises:
+            ValueError: if the generator fails to find a compatible scenario routing.
+        """
+        if antenna_count < 1:
+            raise ValueError("antenna_count must be greater than 0")
+
+        configs = []
+        free_ports = [1, 3, 2, 4]
+        converters_temp = []
+        # First, try to reuse previously used converters where we can.
+        for converter in self.used_once[band]:
+            port = next(
+                (a for a in self.PORT_MAPPING[converter] if a in free_ports),
+                None,
+            )
+            if port is None:
+                # No port available to be used with this converter, save for later.
+                converters_temp.append(converter)
+                continue
+
+            free_ports.remove(port)
+            configs.append(PortConfiguration(port, converter))
+            if len(configs) == antenna_count:
+                break
+
+        self.used_once[band] = converters_temp
+        if len(configs) == antenna_count:
+            return configs
+
+        # Try to use unused converters.
+        for port in free_ports:
+            converter = next(
+                (c for c in self.CONVERTER_MAPPING[port]
+                 if c in self.free_converters),
+                None,
+            )
+            if converter is None:
+                continue
+
+            # Save converter to reuse later.
+            self.free_converters.remove(converter)
+            self.used_once[band].append(converter)
+            configs.append(PortConfiguration(port, converter))
+            if len(configs) == antenna_count:
+                break
+
+        if len(configs) != antenna_count:
+            raise ValueError(
+                "Unable to generate CMW500 scenario for requested combination")
+
+        return configs
diff --git a/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500.py b/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500.py
index 18c5ab3..9b840ed 100644
--- a/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500.py
+++ b/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500.py
@@ -51,6 +51,7 @@
     (17.5, 75),
 ]
 
+
 class DciFormat(Enum):
     """Support DCI Formats for MIMOs."""
     DCI_FORMAT_0 = 1
@@ -74,12 +75,12 @@
 
 class LteBandwidth(Enum):
     """Supported LTE bandwidths."""
-    BANDWIDTH_1MHz = 6 # MHZ_1 is RB_6
-    BANDWIDTH_3MHz = 15 # MHZ_3 is RB_15
-    BANDWIDTH_5MHz = 25 # MHZ_5 is RB_25
-    BANDWIDTH_10MHz = 50 # MHZ_10 is RB_50
-    BANDWIDTH_15MHz = 75 # MHZ_15 is RB_75
-    BANDWIDTH_20MHz = 100 # MHZ_20 is RB_100
+    BANDWIDTH_1MHz = 6  # MHZ_1 is RB_6
+    BANDWIDTH_3MHz = 15  # MHZ_3 is RB_15
+    BANDWIDTH_5MHz = 25  # MHZ_5 is RB_25
+    BANDWIDTH_10MHz = 50  # MHZ_10 is RB_50
+    BANDWIDTH_15MHz = 75  # MHZ_15 is RB_75
+    BANDWIDTH_20MHz = 100  # MHZ_20 is RB_100
 
 
 class LteState(Enum):
@@ -143,15 +144,15 @@
     TM9 = 9
 
 
+# For mimo 1x1, also set_num_crs_antenna_ports to 1
 MIMO_MAX_LAYER_MAPPING = {
-    MimoModes.MIMO1x1: 1,
+    MimoModes.MIMO1x1: 2,
     MimoModes.MIMO2x2: 2,
-    MimoModes.MIMO4x4: 3,
+    MimoModes.MIMO4x4: 4,
 }
 
 
 class Cmx500(abstract_inst.SocketInstrument):
-
     def __init__(self, ip_addr, port, xlapi_path=DEFAULT_XLAPI_PATH):
         """Init method to setup variables for the controller.
 
@@ -167,22 +168,18 @@
         self._initial_xlapi()
         self._settings.system.set_instrument_address(ip_addr)
         logger.info('The instrument address is {}'.format(
-                self._settings.system.get_instrument_address()))
+            self._settings.system.get_instrument_address()))
 
         self.bts = []
 
         # Stops all active cells if there is any
         self.disconnect()
 
-        # loads cell default settings from parameter file if there is one
-        default_setup_path = 'default_cell_setup.rsxp'
-        if path.exists(default_setup_path):
-            self._settings.session.set_test_param_files(default_setup_path)
-
         self.dut = self._network.get_dut()
         self.lte_cell = self._network.create_lte_cell('ltecell0')
         self.nr_cell = self._network.create_nr_cell('nrcell0')
         self._config_antenna_ports()
+
         self.lte_rrc_state_change_timer = DEFAULT_LTE_STATE_CHANGE_TIMER
         self.rrc_state_change_time_enable = False
         self.cell_switch_on_timer = DEFAULT_CELL_SWITCH_ON_TIMER
@@ -260,14 +257,6 @@
     def disconnect(self):
         """Disconnect controller from device and switch to local mode."""
 
-        # Stops all lte and nr_cell
-        for cell in self._network.get_all_lte_cells():
-            if cell.is_on():
-                cell.stop()
-
-        for cell in self._network.get_all_nr_cells():
-            if cell.is_on():
-                cell.stop()
         self.bts.clear()
         self._network.reset()
 
@@ -275,6 +264,25 @@
         """Enable packet switching in call box."""
         raise NotImplementedError()
 
+    def get_cell_configs(self, cell):
+        """Getes cell settings.
+
+        This method is for debugging purpose. In XLAPI, there are many get
+        methods in the cell or component carrier object. When this method is
+        called, the corresponding value could be recorded with logging, which is
+        useful for debug.
+
+        Args:
+            cell: the lte or nr cell in xlapi
+        """
+        cell_getters = [attr for attr in dir(cell) if attr.startswith('get')]
+        for attr in cell_getters:
+            try:
+                getter = getattr(cell, attr)
+                logger.info('The {} is {}'.format(attr, getter()))
+            except Exception as e:
+                logger.warning('Error in get {}: {}'.format(attr, e))
+
     def get_base_station(self, bts_index=0):
         """Gets the base station object based on bts num. By default
         bts_index set to 0 (PCC).
@@ -341,15 +349,23 @@
             for bts in self.bts:
                 if isinstance(bts, LteBaseStation):
                     bts.stop()
-                logger.info(
-                    'The LTE cell status is {} after stop'.format(bts.is_on()))
+                logger.info('The LTE cell status is {} after stop'.format(
+                    bts.is_on()))
 
     def switch_on_nsa_signalling(self):
+
+        from mrtype.counters import N310
+
         if self.bts:
             self.disconnect()
         logger.info('Switches on NSA signalling')
+
+        # Sets n310 timer to N310.N20 to make endc more stable
+        logger.info('set nr cell n310 timer to N310.N20')
+        self.nr_cell.set_n310(N310.N20)
         self.bts.append(LteBaseStation(self, self.lte_cell))
         self.bts.append(NrBaseStation(self, self.nr_cell))
+
         self.bts[0].start()
         lte_cell_status = self.bts[0].wait_cell_on(self.cell_switch_on_timer)
         if lte_cell_status:
@@ -363,6 +379,7 @@
             logger.info('The NR cell status is on')
         else:
             raise CmxError('The NR cell cannot be switched on')
+        time.sleep(5)
 
     def update_lte_cell_config(self, config):
         """Updates lte cell settings with config."""
@@ -407,7 +424,7 @@
                 logger.info('{} reached at {} s'.format(state.value, idx))
                 return True
         error_message = 'Waiting for {} state timeout after {}'.format(
-                state.value, timeout)
+            state.value, timeout)
         logger.error(error_message)
         raise CmxError(error_message)
 
@@ -424,12 +441,19 @@
             self.dut.signaling.wait_for_lte_attach(self.lte_cell, timeout)
         except:
             raise CmxError(
-                    'wait_until_attached timeout after {}'.format(timeout))
+                'wait_until_attached timeout after {}'.format(timeout))
+
+    def send_sms(self, message):
+        """ Sends an SMS message to the DUT.
+
+        Args:
+            message: the SMS message to send.
+        """
+        self.dut.signaling.mt_sms(message)
 
 
 class BaseStation(object):
     """Class to interact with different the base stations."""
-
     def __init__(self, cmx, cell):
         """Init method to setup variables for base station.
 
@@ -473,6 +497,10 @@
         if band.is_dl_only():
             return DuplexMode.DL_ONLY
 
+    def get_cc(self):
+        """Gets component carrier of the cell."""
+        return self._cc
+
     def is_on(self):
         """Verifies if the cell is turned on.
 
@@ -488,6 +516,8 @@
             band: band of cell.
         """
         self._cell.set_band(band)
+        logger.info('The band is set to {} and is {} after setting'.format(
+            band, self.band))
 
     def set_dl_mac_padding(self, state):
         """Enables/Disables downlink padding at the mac layer.
@@ -541,7 +571,6 @@
 
 class LteBaseStation(BaseStation):
     """ LTE base station."""
-
     def __init__(self, cmx, cell):
         """Init method to setup variables for the LTE base station.
 
@@ -551,13 +580,22 @@
         """
         from xlapi.lte_cell import LteCell
         if not isinstance(cell, LteCell):
-            raise CmxError('The cell is not a LTE cell, LTE base station  fails'
-                           ' to create.')
+            raise CmxError(
+                'The cell is not a LTE cell, LTE base station  fails'
+                ' to create.')
         super().__init__(cmx, cell)
 
-    def _config_scheduler(self, dl_mcs=None, dl_rb_alloc=None, dl_dci_ncce=None,
-        dl_dci_format=None, dl_tm=None, dl_num_layers=None, dl_mcs_table=None,
-        ul_mcs=None, ul_rb_alloc=None, ul_dci_ncce=None):
+    def _config_scheduler(self,
+                          dl_mcs=None,
+                          dl_rb_alloc=None,
+                          dl_dci_ncce=None,
+                          dl_dci_format=None,
+                          dl_tm=None,
+                          dl_num_layers=None,
+                          dl_mcs_table=None,
+                          ul_mcs=None,
+                          ul_rb_alloc=None,
+                          ul_dci_ncce=None):
 
         from rs_mrt.testenvironment.signaling.sri.rat.lte import DciFormat
         from rs_mrt.testenvironment.signaling.sri.rat.lte import DlTransmissionMode
@@ -593,30 +631,28 @@
             dl_mcs_table = McsTable(dl_mcs_table)
             log_list.append('dl_mcs_table: {}'.format(dl_mcs_table))
 
-        is_on = self._cell.is_on()
         num_crs_antenna_ports = self._cell.get_num_crs_antenna_ports()
 
         # Sets num of crs antenna ports to 4 for configuring
-        if is_on:
-            self._cell.stop()
-            time.sleep(1)
         self._cell.set_num_crs_antenna_ports(4)
         scheduler = self._cmx.dut.get_scheduler(self._cell)
         logger.info('configure scheduler for {}'.format(','.join(log_list)))
-        scheduler.configure_scheduler(
-                dl_mcs=dl_mcs, dl_rb_alloc=dl_rb_alloc, dl_dci_ncce=dl_dci_ncce,
-                dl_dci_format=dl_dci_format, dl_tm=dl_tm,
-                dl_num_layers=dl_num_layers, dl_mcs_table=dl_mcs_table,
-                ul_mcs=ul_mcs, ul_rb_alloc=ul_rb_alloc, ul_dci_ncce=ul_dci_ncce)
+        scheduler.configure_scheduler(dl_mcs=dl_mcs,
+                                      dl_rb_alloc=dl_rb_alloc,
+                                      dl_dci_ncce=dl_dci_ncce,
+                                      dl_dci_format=dl_dci_format,
+                                      dl_tm=dl_tm,
+                                      dl_num_layers=dl_num_layers,
+                                      dl_mcs_table=dl_mcs_table,
+                                      ul_mcs=ul_mcs,
+                                      ul_rb_alloc=ul_rb_alloc,
+                                      ul_dci_ncce=ul_dci_ncce)
         logger.info('Configure scheduler succeeds')
 
         # Sets num of crs antenna ports back to previous value
         self._cell.set_num_crs_antenna_ports(num_crs_antenna_ports)
         self._network.apply_changes()
 
-        if is_on:
-            self._cell.start()
-
     @property
     def bandwidth(self):
         """Get the channel bandwidth of the cell.
@@ -640,7 +676,7 @@
         """Get the downlink frequency of the cell."""
         from mrtype.frequency import Frequency
         return self._cell.get_dl_earfcn().to_freq().in_units(
-                Frequency.Units.GHz)
+            Frequency.Units.GHz)
 
     def _to_rb_bandwidth(self, bandwidth):
         for idx in range(5):
@@ -648,6 +684,11 @@
                 return LTE_MHZ_UPPER_BOUND_TO_RB[idx][1]
         return 100
 
+    def disable_all_ul_subframes(self):
+        """Disables all ul subframes for LTE cell."""
+        self._cc.disable_all_ul_subframes()
+        self._network.apply_changes()
+
     def set_bandwidth(self, bandwidth):
         """Sets the channel bandwidth of the cell.
 
@@ -655,6 +696,28 @@
             bandwidth: channel bandwidth of cell in MHz.
         """
         self._cell.set_bandwidth(self._to_rb_bandwidth(bandwidth))
+        self._network.apply_changes()
+
+    def set_cdrx_config(self):
+        """Sets LTE cdrx config for endc."""
+        from mrtype.lte.drx import (
+            LteDrxConfig,
+            LteDrxInactivityTimer,
+            LteDrxLongCycleStartOffset,
+            LteDrxOnDurationTimer,
+            LteDrxRetransmissionTimer,
+        )
+
+        logger.info('Config Lte drx config')
+        lte_drx_config = LteDrxConfig(
+            on_duration_timer=LteDrxOnDurationTimer.PSF_10,
+            inactivity_timer=LteDrxInactivityTimer.PSF_200,
+            retransmission_timer=LteDrxRetransmissionTimer.PSF_33,
+            long_cycle_start_offset=LteDrxLongCycleStartOffset.ms160(83),
+            short_drx=None)
+        self._cmx.dut.lte_cell_group().set_drx_and_adjust_scheduler(
+            drx_config=lte_drx_config)
+        self._network.apply_changes()
 
     def set_cell_frequency_band(self, tdd_cfg=None, ssf_cfg=None):
         """Sets cell frequency band with tdd and ssf config.
@@ -674,8 +737,8 @@
         if ssf_cfg:
             ssf_pattern = SpecialSubframePattern(ssf_cfg)
         tdd = Tdd(tdd_config=Tdd.TddConfigSignaling(
-                subframe_assignment=tdd_subframe,
-                special_subframe_pattern=ssf_pattern))
+            subframe_assignment=tdd_subframe,
+            special_subframe_pattern=ssf_pattern))
         self._cell.stub.SetCellFrequencyBand(CellFrequencyBand(tdd=tdd))
         self._network.apply_changes()
 
@@ -689,7 +752,7 @@
         from rs_mrt.testenvironment.signaling.sri.rat.lte.config import PdcchRegionReq
 
         logger.info('The cfi enum to set is {}'.format(
-                NumberOfPdcchSymbols(cfi)))
+            NumberOfPdcchSymbols(cfi)))
         req = PdcchRegionReq()
         req.num_pdcch_symbols = NumberOfPdcchSymbols(cfi)
         self._cell.stub.SetPdcchControlRegion(req)
@@ -735,16 +798,14 @@
         if not isinstance(mimo, MimoModes):
             raise CmxError("Wrong type of mimo mode")
 
-        is_on = self._cell.is_on()
-        if is_on:
-            self._cell.stop()
         self._cell.set_num_crs_antenna_ports(mimo.value)
         self._config_scheduler(dl_num_layers=MIMO_MAX_LAYER_MAPPING[mimo])
-        if is_on:
-            self._cell.start()
 
-    def set_scheduling_mode(
-        self, mcs_dl=None, mcs_ul=None, nrb_dl=None, nrb_ul=None):
+    def set_scheduling_mode(self,
+                            mcs_dl=None,
+                            mcs_ul=None,
+                            nrb_dl=None,
+                            nrb_ul=None):
         """Sets scheduling mode.
 
         Args:
@@ -754,8 +815,10 @@
             nrb_dl: Number of RBs for downlink.
             nrb_ul: Number of RBs for uplink.
         """
-        self._config_scheduler(dl_mcs=mcs_dl, ul_mcs=mcs_ul, dl_rb_alloc=nrb_dl,
-                ul_rb_alloc=nrb_ul)
+        self._config_scheduler(dl_mcs=mcs_dl,
+                               ul_mcs=mcs_ul,
+                               dl_rb_alloc=nrb_dl,
+                               ul_rb_alloc=nrb_ul)
 
     def set_ssf_config(self, ssf_config):
         """Sets ssf subframe assignment with tdd_config.
@@ -779,9 +842,13 @@
         Args:
             transmission_mode: the download link transmission mode.
         """
+        from rs_mrt.testenvironment.signaling.sri.rat.lte import DlTransmissionMode
         if not isinstance(transmission_mode, TransmissionModes):
             raise CmxError('Wrong type of the trasmission mode')
-        self._config_scheduler(dl_tm=transmission_mode)
+        dl_tm = DlTransmissionMode(transmission_mode.value)
+        logger.info('set dl tm to {}'.format(dl_tm))
+        self._cc.set_dl_tm(dl_tm)
+        self._network.apply_changes()
 
     def set_ul_channel(self, channel):
         """Sets the up link channel number of cell.
@@ -813,7 +880,7 @@
         """
         from mrtype.frequency import Frequency
         return self._cell.get_ul_earfcn().to_freq().in_units(
-                Frequency.Units.GHz)
+            Frequency.Units.GHz)
 
     def set_ul_modulation_table(self, modulation):
         """Sets up link modulation table.
@@ -831,7 +898,6 @@
 
 class NrBaseStation(BaseStation):
     """ NR base station."""
-
     def __init__(self, cmx, cell):
         """Init method to setup variables for the NR base station.
 
@@ -846,9 +912,14 @@
 
         super().__init__(cmx, cell)
 
-    def _config_scheduler(self, dl_mcs=None, dl_mcs_table=None,
-                          dl_rb_alloc=None, dl_mimo_mode=None,
-                          ul_mcs=None, ul_mcs_table=None, ul_rb_alloc=None,
+    def _config_scheduler(self,
+                          dl_mcs=None,
+                          dl_mcs_table=None,
+                          dl_rb_alloc=None,
+                          dl_mimo_mode=None,
+                          ul_mcs=None,
+                          ul_mcs_table=None,
+                          ul_rb_alloc=None,
                           ul_mimo_mode=None):
 
         from rs_mrt.testenvironment.signaling.sri.rat.nr import McsTable
@@ -879,24 +950,20 @@
         if ul_mimo_mode:
             log_list.append('ul_mimo_mode: {}'.format(ul_mimo_mode))
 
-        is_on = self._cell.is_on()
-        if is_on:
-            self._cell.stop()
-            time.sleep(1)
         scheduler = self._cmx.dut.get_scheduler(self._cell)
         logger.info('configure scheduler for {}'.format(','.join(log_list)))
 
-        scheduler.configure_ue_scheduler(
-                dl_mcs=dl_mcs, dl_mcs_table=dl_mcs_table,
-                dl_rb_alloc=dl_rb_alloc, dl_mimo_mode=dl_mimo_mode,
-                ul_mcs=ul_mcs, ul_mcs_table=ul_mcs_table,
-                ul_rb_alloc=ul_rb_alloc, ul_mimo_mode=ul_mimo_mode)
+        scheduler.configure_ue_scheduler(dl_mcs=dl_mcs,
+                                         dl_mcs_table=dl_mcs_table,
+                                         dl_rb_alloc=dl_rb_alloc,
+                                         dl_mimo_mode=dl_mimo_mode,
+                                         ul_mcs=ul_mcs,
+                                         ul_mcs_table=ul_mcs_table,
+                                         ul_rb_alloc=ul_rb_alloc,
+                                         ul_mimo_mode=ul_mimo_mode)
         logger.info('Configure scheduler succeeds')
         self._network.apply_changes()
 
-        if is_on:
-            self._cell.start()
-
     def attach_as_secondary_cell(self, endc_timer=DEFAULT_ENDC_TIMER):
         """Enable endc mode for NR cell.
 
@@ -917,12 +984,28 @@
                 logger.info('did not reach endc at {} s'.format(time_count))
         raise CmxError('Cannot reach endc after {} s'.format(endc_timer))
 
+    def config_flexible_slots(self):
+        """Configs flexible slots for NR cell."""
+
+        from rs_mrt.testenvironment.signaling.sri.rat.nr.config import CellFrequencyBandReq
+        from rs_mrt.testenvironment.signaling.sri.rat.common import SetupRelease
+
+        logger.info('Config flexible slots')
+        req = CellFrequencyBandReq()
+        req.band.frequency_range.fr1.tdd.tdd_config_common.setup_release = SetupRelease.RELEASE
+        self._cell.stub.SetCellFrequencyBand(req)
+        self._network.apply_changes()
+
+    def disable_all_ul_slots(self):
+        """Disables all ul slots for NR cell"""
+        self._cc.disable_all_ul_slots()
+
     @property
     def dl_channel(self):
         """Gets the downlink channel of cell.
 
         Return:
-            the downlink channel (earfcn) in int.
+            the downlink channel (nr_arfcn) in int.
         """
         return int(self._cell.get_dl_ref_a())
 
@@ -958,27 +1041,6 @@
         else:
             return CarrierBandwidth(bandwidth // 5 - 1)
 
-    def set_band(self, band, frequency_range=None):
-        """Sets the Band of cell.
-
-        Args:
-            band: band of cell.
-            frequency_range: LOW, MID and HIGH for NR cell
-        """
-        from mrtype.frequency import FrequencyRange
-        if not frequency_range or frequency_range.upper() == 'LOW':
-            frequency_range = FrequencyRange.LOW
-        elif frequency_range.upper() == 'MID':
-            frequency_range = FrequencyRange.MID
-        elif frequency_range.upper() == 'HIGH':
-            frequency_range = FrequencyRange.HIGH
-        else:
-            raise CmxError('Wrong type FrequencyRange')
-
-        self._cell.set_dl_ref_a_offset(band, frequency_range)
-        logger.info('The band is set to {} and is {} after setting'.format(
-                band, self.band))
-
     def set_bandwidth(self, bandwidth, scs=None):
         """Sets the channel bandwidth of the cell.
 
@@ -989,17 +1051,32 @@
         if not scs:
             scs = self._cell.get_scs()
         self._cell.set_carrier_bandwidth_and_scs(
-                self._bandwidth_to_carrier_bandwidth(bandwidth), scs)
-        logger.info('The bandwidth in MHz is {}. After setting, the value is {}'
-                    .format(bandwidth, str(self._cell.get_carrier_bandwidth())))
+            self._bandwidth_to_carrier_bandwidth(bandwidth), scs)
+        logger.info(
+            'The bandwidth in MHz is {}. After setting, the value is {}'.
+            format(bandwidth, str(self._cell.get_carrier_bandwidth())))
 
     def set_dl_channel(self, channel):
         """Sets the downlink channel number of cell.
 
         Args:
-            channel: downlink channel number of cell.
+            channel: downlink channel number of cell or Frequency Range ('LOW',
+                     'MID' or 'HIGH').
         """
         from mrtype.nr.frequency import NrArfcn
+        from mrtype.frequency import FrequencyRange
+
+        # When the channel is string, set it use Frenquency Range
+        if isinstance(channel, str):
+            logger.info('Sets dl channel Frequency Range {}'.format(channel))
+            frequency_range = FrequencyRange.LOW
+            if channel.upper() == 'MID':
+                frequency_range = FrequencyRange.MID
+            elif channel.upper() == 'HIGH':
+                frequency_range = FrequencyRange.HIGH
+            self._cell.set_dl_ref_a_offset(self.band, frequency_range)
+            logger.info('The dl_channel was set to {}'.format(self.dl_channel))
+            return
         if self.dl_channel == channel:
             logger.info('The dl_channel was at {}'.format(self.dl_channel))
             return
@@ -1026,15 +1103,13 @@
         if not isinstance(mimo, MimoModes):
             raise CmxError("Wrong type of mimo mode")
 
-        is_on = self._cell.is_on()
-        if is_on:
-            self._cell.stop()
-        self._cc.set_dl_mimo_mode(DownlinkMimoMode.Enum(mimo.value))
-        if is_on:
-            self._cell.start()
+        self._config_scheduler(dl_mimo_mode=DownlinkMimoMode.Enum(mimo.value))
 
-    def set_scheduling_mode(
-        self, mcs_dl=None, mcs_ul=None, nrb_dl=None, nrb_ul=None):
+    def set_scheduling_mode(self,
+                            mcs_dl=None,
+                            mcs_ul=None,
+                            nrb_dl=None,
+                            nrb_ul=None):
         """Sets scheduling mode.
 
         Args:
@@ -1043,8 +1118,10 @@
             nrb_dl: Number of RBs for downlink.
             nrb_ul: Number of RBs for uplink.
         """
-        self._config_scheduler(dl_mcs=mcs_dl, ul_mcs=mcs_ul, dl_rb_alloc=nrb_dl,
-                ul_rb_alloc=nrb_ul)
+        self._config_scheduler(dl_mcs=mcs_dl,
+                               ul_mcs=mcs_ul,
+                               dl_rb_alloc=nrb_dl,
+                               ul_rb_alloc=nrb_ul)
 
     def set_ssf_config(self, ssf_config):
         """Sets ssf subframe assignment with tdd_config.
diff --git a/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500_cellular_simulator.py b/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500_cellular_simulator.py
index ca281d1..6bc157a 100644
--- a/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500_cellular_simulator.py
+++ b/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500_cellular_simulator.py
@@ -14,14 +14,15 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import logging
+import time
+from enum import Enum
+
 from acts.controllers.rohdeschwarz_lib import cmx500
 from acts.controllers.rohdeschwarz_lib.cmx500 import LteBandwidth
 from acts.controllers.rohdeschwarz_lib.cmx500 import LteState
 from acts.controllers import cellular_simulator as cc
 from acts.controllers.cellular_lib import LteSimulation
 
-
 CMX_TM_MAPPING = {
     LteSimulation.TransmissionMode.TM1: cmx500.TransmissionModes.TM1,
     LteSimulation.TransmissionMode.TM2: cmx500.TransmissionModes.TM2,
@@ -43,16 +44,27 @@
 }
 
 
+class ConfigurationMode(Enum):
+    Power = "Power"
+
+
 class CMX500CellularSimulator(cc.AbstractCellularSimulator):
     """ A cellular simulator for telephony simulations based on the CMX 500
     controller. """
 
-    def __init__(self, ip_address, port='5025'):
+    # The maximum power that the equipment is able to transmit
+    MAX_DL_POWER = -25
+
+    def __init__(self,
+                 ip_address,
+                 port='5025',
+                 config_mode=ConfigurationMode.Power):
         """ Initializes the cellular simulator.
 
         Args:
             ip_address: the ip address of the CMX500
             port: the port number for the CMX500 controller
+            config_mode: A pre-defined configuration mode to use.
         """
         super().__init__()
         try:
@@ -60,6 +72,7 @@
         except:
             raise cc.CellularSimulatorError('Error when Initializes CMX500.')
 
+        self._config_mode = config_mode
         self.bts = self.cmx.bts
 
     def destroy(self):
@@ -82,11 +95,12 @@
         self.log.info('setup nsa scenario (start lte cell and nr cell')
         self.cmx.switch_on_nsa_signalling()
 
-    def set_band_combination(self, bands):
-        """ Prepares the test equipment for the indicated band combination.
+    def set_band_combination(self, bands, mimo_modes):
+        """ Prepares the test equipment for the indicated band/mimo combination.
 
         Args:
             bands: a list of bands represented as ints or strings
+            mimo_modes: a list of LteSimulation.MimoMode to use for each carrier
         """
         self.num_carriers = len(bands)
 
@@ -98,12 +112,11 @@
             time: time in seconds for the timer to expire
         """
         self.log.info('set timer enabled to {} and the time to {}'.format(
-                enabled, time))
+            enabled, time))
         self.cmx.rrc_state_change_time_enable = enabled
         self.cmx.lte_rrc_state_change_timer = time
 
-
-    def set_band(self, bts_index, band, frequency_range=None):
+    def set_band(self, bts_index, band):
         """ Sets the band for the indicated base station.
 
         Args:
@@ -111,11 +124,7 @@
             band: the new band
         """
         self.log.info('set band to {}'.format(band))
-        if frequency_range:
-            self.bts[bts_index].set_band(
-                    int(band), frequency_range=frequency_range)
-        else:
-            self.bts[bts_index].set_band(int(band))
+        self.bts[bts_index].set_band(int(band))
 
     def get_duplex_mode(self, band):
         """ Determines if the band uses FDD or TDD duplex mode
@@ -184,7 +193,7 @@
             bandwidth: the new bandwidth in MHz
         """
         self.log.info('set bandwidth of bts {} to {}'.format(
-                bts_index, bandwidth))
+            bts_index, bandwidth))
         self.bts[bts_index].set_bandwidth(int(bandwidth))
 
     def set_downlink_channel_number(self, bts_index, channel_number):
@@ -194,8 +203,8 @@
             bts_index: the base station number
             channel_number: the new channel number (earfcn)
         """
-        self.log.info('Sets the downlink channel number to {}'.format(
-                channel_number))
+        self.log.info(
+            'Sets the downlink channel number to {}'.format(channel_number))
         self.bts[bts_index].set_dl_channel(channel_number)
 
     def set_mimo_mode(self, bts_index, mimo_mode):
@@ -220,8 +229,13 @@
         tmode = CMX_TM_MAPPING[tmode]
         self.bts[bts_index].set_transmission_mode(tmode)
 
-    def set_scheduling_mode(self, bts_index, scheduling, mcs_dl=None,
-                            mcs_ul=None, nrb_dl=None, nrb_ul=None):
+    def set_scheduling_mode(self,
+                            bts_index,
+                            scheduling,
+                            mcs_dl=None,
+                            mcs_ul=None,
+                            nrb_dl=None,
+                            nrb_ul=None):
         """ Sets the scheduling mode for the indicated base station.
 
         Args:
@@ -246,8 +260,10 @@
             log_list.append('nrb_ul: {}'.format(nrb_ul))
 
         self.log.info('set scheduling mode to {}'.format(','.join(log_list)))
-        self.bts[bts_index].set_scheduling_mode(
-                mcs_dl=mcs_dl, mcs_ul=mcs_ul, nrb_dl=nrb_dl, nrb_ul=nrb_ul)
+        self.bts[bts_index].set_scheduling_mode(mcs_dl=mcs_dl,
+                                                mcs_ul=mcs_ul,
+                                                nrb_dl=nrb_dl,
+                                                nrb_ul=nrb_ul)
 
     def set_dl_256_qam_enabled(self, bts_index, enabled):
         """ Determines what MCS table should be used for the downlink.
@@ -331,6 +347,36 @@
         """
         self.wait_until_communication_state()
         self.bts[1].attach_as_secondary_cell()
+        time.sleep(10)
+
+        if self._config_mode and self._config_mode == ConfigurationMode.Power:
+            self.configure_for_power_measurement()
+
+        self.log.info('The radio connectivity is {}'.format(
+            self.cmx.dut.state.radio_connectivity))
+
+    def configure_for_power_measurement(self):
+        """ Applies a pre-defined configuration for PDCCH power testing."""
+        self.log.info('set lte cdrx for nr nsa scenario')
+        self.bts[0].set_cdrx_config()
+        time.sleep(5)
+
+        self.log.info('Disables mac padding')
+        self.bts[0].set_dl_mac_padding(False)
+        self.bts[1].set_dl_mac_padding(False)
+        time.sleep(5)
+
+        self.log.info('configure flexible slots and wait for 5 seconds')
+        self.bts[1].config_flexible_slots()
+        time.sleep(5)
+
+        self.log.info('disable all ul subframes of the lte cell')
+        self.bts[0].disable_all_ul_subframes()
+        time.sleep(30)
+
+        self.log.info('Disables Nr UL slots')
+        self.bts[1].disable_all_ul_slots()
+        time.sleep(5)
 
     def wait_until_attached(self, timeout=120):
         """ Waits until the DUT is attached to the primary carrier.
@@ -370,6 +416,15 @@
         self.log.info('wait for rrc off state')
         return self.cmx.wait_for_rrc_state(cmx500.RrcState.RRC_OFF, timeout)
 
+    def wait_until_quiet(self, timeout=120):
+        """Waits for all pending operations to finish on the simulator.
+
+        Args:
+            timeout: after this amount of time the method will raise a
+                CellularSimulatorError exception. Default is 120 seconds.
+        """
+        self.cmx._network.apply_changes()
+
     def detach(self):
         """ Turns off all the base stations so the DUT loose connection."""
         self.log.info('Bypass simulator detach step for now')
@@ -387,3 +442,11 @@
     def stop_data_traffic(self):
         """ Stops transmitting data from the instrument to the DUT. """
         self.log.warning('The stop_data_traffic is not implemented yet')
+
+    def send_sms(self, message):
+        """ Sends an SMS message to the DUT.
+
+        Args:
+            message: the SMS message to send.
+        """
+        self.cmx.send_sms(message)
diff --git a/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500_events.py b/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500_events.py
new file mode 100644
index 0000000..f4abb0c
--- /dev/null
+++ b/acts/framework/acts/controllers/rohdeschwarz_lib/cmx500_events.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2023 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#           http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts import logger as acts_logger
+
+logger = acts_logger.create_logger()
+
+
+def on_emm_registered(callback):
+    """Registers a callback to watch for EMM attach events.
+
+    Args:
+        callback: a callback to be invoked on EMM attach events.
+
+    Returns:
+        cancel: a callback to deregister the event watcher.
+    """
+    from rs_mrt.testenvironment.signaling.sri.nas.eps.pubsub import EmmAttachPub
+    from rs_mrt.testenvironment.signaling.sri.nas.eps import EmmRegistrationState
+
+    def wrapped(msg):
+        logger.debug("CMX received EMM registration state: {}".format(
+            msg.registration_state))
+        if msg.registration_state in (
+                EmmRegistrationState.COMBINED_REGISTERED, ):
+            callback()
+
+    sub = EmmAttachPub.multi_subscribe(callback=wrapped)
+
+    return lambda: sub.unsubscribe()
diff --git a/acts/framework/acts/controllers/sniffer.py b/acts/framework/acts/controllers/sniffer.py
index dab7e90..d68a398 100644
--- a/acts/framework/acts/controllers/sniffer.py
+++ b/acts/framework/acts/controllers/sniffer.py
@@ -43,9 +43,10 @@
         module_name = "acts.controllers.sniffer_lib.{}.{}".format(
             sniffer_type, sniffer_subtype)
         module = importlib.import_module(module_name)
-        objs.append(module.Sniffer(interface,
-                                   logging.getLogger(),
-                                   base_configs=base_configs))
+        objs.append(
+            module.Sniffer(interface,
+                           logging.getLogger(),
+                           base_configs=base_configs))
     return objs
 
 
@@ -63,14 +64,12 @@
     """This is the Exception class defined for all errors generated by
     Sniffer-related modules.
     """
-    pass
 
 
 class InvalidDataError(Exception):
     """This exception is thrown when invalid configuration data is passed
     to a method.
     """
-    pass
 
 
 class ExecutionError(SnifferError):
@@ -81,7 +80,6 @@
     without sudo (for local sniffers) or keys are out-of-date (for remote
     sniffers).
     """
-    pass
 
 
 class InvalidOperationError(SnifferError):
@@ -89,7 +87,6 @@
     are invoked is in a certain state. This indicates that the object is not
     in the correct state for a method to be called.
     """
-    pass
 
 
 class Sniffer(object):
diff --git a/acts/framework/acts/controllers/spirent_lib/gss7000.py b/acts/framework/acts/controllers/spirent_lib/gss7000.py
index 961d4e8..3f463fc 100644
--- a/acts/framework/acts/controllers/spirent_lib/gss7000.py
+++ b/acts/framework/acts/controllers/spirent_lib/gss7000.py
@@ -162,7 +162,7 @@
                     Type, list.
         """
         root = ET.fromstring(xml)
-        capability_ls = list()
+        capability_ls = []
         sig_cap_list = root.find('data').find('Signal_capabilities').findall(
             'Signal')
         for signal in sig_cap_list:
@@ -203,15 +203,13 @@
         if scenario == '':
             errmsg = ('Missing scenario file')
             raise GSS7000Error(error=errmsg, command='load_scenario')
-        else:
-            self._logger.debug('Stopped the original scenario')
-            self._query('-,EN,1')
-            cmd = 'SC,' + scenario
-            self._logger.debug('Loading scenario')
-            self._query(cmd)
-            self._logger.debug('Scenario is loaded')
-            return True
-        return False
+        self._logger.debug('Stopped the original scenario')
+        self._query('-,EN,1')
+        cmd = 'SC,' + scenario
+        self._logger.debug('Loading scenario')
+        self._query(cmd)
+        self._logger.debug('Scenario is loaded')
+        return True
 
     def start_scenario(self, scenario=''):
         """Load and Start the running scenario.
@@ -223,6 +221,8 @@
         if scenario:
             if self.load_scenario(scenario):
                 self._query('RU')
+        # TODO: Need to refactor the logic design to solve the comment in ag/19222896
+        # Track the issue in b/241200605
             else:
                 infmsg = 'No scenario is loaded. Stop running scenario'
                 self._logger.debug(infmsg)
@@ -230,7 +230,7 @@
             pass
 
         if scenario:
-            infmsg = 'Started running scenario {}'.format(scenario)
+            infmsg = f'Started running scenario {scenario}'
         else:
             infmsg = 'Started running current scenario'
 
@@ -279,12 +279,12 @@
             GSS7000Error: raise when power offset level is not in [-170, -115] range.
         """
         if not -170 <= ref_dBm <= -115:
-            errmsg = ('"power_offset" must be within [-170, -115], '
-                      'current input is {}').format(str(ref_dBm))
+            errmsg = (f'"power_offset" must be within [-170, -115], '
+                      f'current input is {ref_dBm}')
             raise GSS7000Error(error=errmsg, command='set_ref_power')
-        cmd = 'REF_DBM,{}'.format(str(round(ref_dBm, 1)))
+        cmd = f'REF_DBM,{ref_dBm:.1f}'
         self._query(cmd)
-        infmsg = 'Set reference power level: {}'.format(str(round(ref_dBm, 1)))
+        infmsg = f'Set reference power level: {ref_dBm:.1f}'
         self._logger.debug(infmsg)
 
     def get_status(self, return_txt=False):
@@ -309,8 +309,7 @@
                      'Waiting for further commands.'
             }
             return status_dict.get(status)
-        else:
-            return int(status)
+        return int(status)
 
     def set_power(self, power_level=-130):
         """Set Power Level of GSS7000 Tx
@@ -331,8 +330,7 @@
         self.set_power_offset(1, power_offset)
         self.set_power_offset(2, power_offset)
 
-        infmsg = 'Set GSS7000 transmit power to "{}"'.format(
-            round(power_level, 1))
+        infmsg = f'Set GSS7000 transmit power to "{power_level:.1f}"'
         self._logger.debug(infmsg)
 
     def power_lev_offset_cal(self, power_level=-130, sat='GPS', band='L1'):
@@ -418,8 +416,8 @@
                 f'Satellite system and band ({sat_band}) are not supported.'
                 f'The GSS7000 support list: {self.capability}')
             raise GSS7000Error(error=errmsg, command='set_scenario_power')
-        else:
-            sat_band_tp = tuple(sat_band.split('_'))
+
+        sat_band_tp = tuple(sat_band.split('_'))
 
         return sat_band_tp
 
@@ -436,14 +434,14 @@
                 Default. -130
             sat_id: set power level for specific satellite identifiers
                 Type, int.
-            sat_system: to set power level for all Satellites
+            sat_system: to set power level for specific system
                 Type, str
                 Option 'GPS/GLO/GAL/BDS'
                 Type, str
                 Default, '', assumed to be GPS.
             freq_band: Frequency band to set the power level
                 Type, str
-                Option 'L1/L5/B1I/B1C/B2A/F1/E5/ALL'
+                Option 'L1/L5/B1I/B1C/B2A/F1/E5'
                 Default, '', assumed to be L1.
         Raises:
             GSS7000Error: raise when power offset is not in [-49, -15] range.
@@ -455,8 +453,7 @@
             'B1I': 1,
             'B1C': 1,
             'F1': 1,
-            'E5': 2,
-            'ALL': 3
+            'E5': 2
         }
 
         # Convert and check satellite system and band
@@ -464,12 +461,13 @@
         # Get freq band setting
         band_cmd = band_dict.get(band, 1)
 
+        # When set sat_id --> control specific SV power.
+        # When set is not set --> control all SVs of specific system power.
         if not sat_id:
-            sat_id = 0
+            sat_id = 1
             all_tx_type = 1
         else:
             all_tx_type = 0
-
         # Convert absolute power level to absolute power offset.
         power_offset = self.power_lev_offset_cal(power_level, sat, band)
 
@@ -478,13 +476,10 @@
                       f'current input is {power_offset}')
             raise GSS7000Error(error=errmsg, command='set_power_offset')
 
+        # If no specific sat_system is set, the default is GPS L1.
         if band_cmd == 1:
             cmd = f'-,POW_LEV,v1_a1,{power_offset},{sat},{sat_id},0,0,0,1,1,{all_tx_type}'
             self._query(cmd)
         elif band_cmd == 2:
             cmd = f'-,POW_LEV,v1_a2,{power_offset},{sat},{sat_id},0,0,0,1,1,{all_tx_type}'
             self._query(cmd)
-        elif band_cmd == 3:
-            cmd = f'-,POW_LEV,v1_a1,{power_offset},{sat},{sat_id},0,0,0,1,1,{all_tx_type}'
-            self._query(cmd)
-            cmd = f'-,POW_LEV,v1_a2,{power_offset},{sat},{sat_id},0,0,0,1,1,{all_tx_type}'
diff --git a/acts/framework/acts/controllers/tigertail.py b/acts/framework/acts/controllers/tigertail.py
index 3a0ff6a..5f582ae 100644
--- a/acts/framework/acts/controllers/tigertail.py
+++ b/acts/framework/acts/controllers/tigertail.py
@@ -1,7 +1,6 @@
 """Module manager the required definitions for tigertail"""
 
 import logging
-import os
 import time
 
 from enum import Enum
@@ -13,6 +12,7 @@
 
 TIGERTAIL_SLEEP_TIME = 5
 
+
 def create(configs):
     """Takes a list of Tigertail serial numbers and returns Tigertail Controllers.
 
@@ -31,7 +31,8 @@
             tigertail = Tigertail(serial_no)
             tigertails.append(tigertail)
     else:
-        raise ValueError('Invalid config for tigertail, should be a list of serial number')
+        raise ValueError(
+            'Invalid config for tigertail, should be a list of serial number')
 
     return tigertails
 
@@ -39,19 +40,24 @@
 def destroy(tigertails):
     pass
 
+
 def get_info(tigertails):
     return [tigertail.get_info() for tigertail in tigertails]
 
+
 class TigertailError(Exception):
     pass
 
+
 class TigertailState(Enum):
     def __str__(self):
         return str(self.value)
+
     A = 'A'
     B = 'B'
     Off = 'off'
 
+
 class Tigertail(object):
     def __init__(self, serial_number):
         self.serial_number = serial_number
@@ -82,7 +88,8 @@
         if self.tigertool_bin is None:
             raise TigertailError('Tigertail binary not found')
 
-        logging.getLogger().debug(f'Setup {self.serial_number} with binary at {self.tigertool_bin}')
+        logging.getLogger().debug(
+            f'Setup {self.serial_number} with binary at {self.tigertool_bin}')
 
     def turn_on_mux_A(self):
         self._set_tigertail_state(TigertailState.A)
@@ -102,12 +109,12 @@
             B  : enable port B
             Off: turn off both ports
         """
-        result = job.run([self.tigertool_bin,
-            '--serialno',
-            str(self.serial_number),
-            '--mux',
-            str(state)],
-            timeout=10)
+        result = job.run([
+            self.tigertool_bin, '--serialno',
+            str(self.serial_number), '--mux',
+            str(state)
+        ],
+                         timeout=10)
 
         if result.stderr != '':
             raise TigertailError(result.stderr)
diff --git a/acts/framework/acts/controllers/uxm_lib/OWNERS b/acts/framework/acts/controllers/uxm_lib/OWNERS
new file mode 100644
index 0000000..0c40622
--- /dev/null
+++ b/acts/framework/acts/controllers/uxm_lib/OWNERS
@@ -0,0 +1,3 @@
+jethier@google.com
+hmtuan@google.com
+harjani@google.com
\ No newline at end of file
diff --git a/acts/framework/acts/controllers/uxm_lib/uxm_cellular_simulator.py b/acts/framework/acts/controllers/uxm_lib/uxm_cellular_simulator.py
new file mode 100644
index 0000000..5ef7eac
--- /dev/null
+++ b/acts/framework/acts/controllers/uxm_lib/uxm_cellular_simulator.py
@@ -0,0 +1,1005 @@
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT 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 re
+import logging
+import os
+import paramiko
+import socket
+import time
+
+from acts.controllers.cellular_simulator import AbstractCellularSimulator
+
+class SocketWrapper():
+    """A wrapper for socket communicate with test equipment.
+
+    Attributes:
+        _socket: a socket object.
+        _ip: a string value for ip address
+            which we want to connect.
+        _port: an integer for port
+            which we want to connect.
+        _connecting_timeout: an integer for socket connecting timeout.
+        _encode_format: a string specify encoding format.
+        _cmd_terminator: a character indicates the end of command/data
+            which need to be sent.
+    """
+
+    def __init__(self, ip, port,
+                 connecting_timeout=120,
+                 cmd_terminator='\n',
+                 encode_format='utf-8',
+                 buff_size=1024):
+        self._socket = None
+        self._ip = ip
+        self._port = port
+        self._connecting_timeout = connecting_timeout
+        self._cmd_terminator = cmd_terminator
+        self._encode_format = encode_format
+        self._buff_size = buff_size
+        self._logger = logging.getLogger(__name__)
+
+    def _connect(self):
+        self._socket = socket.create_connection(
+            (self._ip, self._port), timeout=self._connecting_timeout
+        )
+
+    def send_command(self, cmd: str):
+        if not self._socket:
+            self._connect()
+        if cmd and cmd[-1] != self._cmd_terminator:
+            cmd = cmd + self._cmd_terminator
+        self._socket.sendall(cmd.encode(self._encode_format))
+
+    def send_command_recv(self, cmd: str) -> str:
+        """Send data and wait for response
+
+        Args:
+            cmd: a string command to be sent.
+
+        Returns:
+            a string response.
+        """
+        self.send_command(cmd)
+        response = ''
+        try:
+            response = self._socket.recv(self._buff_size).decode(
+                self._encode_format
+            )
+        except socket.timeout as e:
+            self._logger.info('Socket timeout while receiving response.')
+            self.close()
+            raise
+
+        return response
+
+    def close(self):
+        self._socket.close()
+        self._socket = None
+
+class UXMCellularSimulator(AbstractCellularSimulator):
+    """A cellular simulator for UXM callbox."""
+
+    # Keys to obtain data from cell_info dictionary.
+    KEY_CELL_NUMBER = "cell_number"
+    KEY_CELL_TYPE = "cell_type"
+
+    # UXM socket port
+    UXM_SOCKET_PORT = 5125
+
+    # UXM SCPI COMMAND
+    SCPI_IMPORT_STATUS_QUERY_CMD = 'SYSTem:SCPI:IMPort:STATus?'
+    SCPI_SYSTEM_ERROR_CHECK_CMD = 'SYST:ERR?\n'
+    SCPI_CHECK_CONNECTION_CMD = '*IDN?\n'
+    SCPI_DEREGISTER_UE_IMS = 'SYSTem:IMS:SERVer:UE:DERegister'
+    # require: path to SCPI file
+    SCPI_IMPORT_SCPI_FILE_CMD = 'SYSTem:SCPI:IMPort "{}"\n'
+    # require: 1. cell type (E.g. NR5G), 2. cell number (E.g CELL1)
+    SCPI_CELL_ON_CMD = 'BSE:CONFig:{}:{}:ACTive 1'
+    SCPI_CELL_OFF_CMD = 'BSE:CONFig:{}:{}:ACTive 0'
+    SCPI_GET_CELL_STATUS = 'BSE:STATus:{}:{}?'
+    SCPI_RRC_RELEASE_LTE_CMD = 'BSE:FUNCtion:{}:{}:RELease:SEND'
+    SCPI_RRC_RELEASE_NR_CMD = 'BSE:CONFig:{}:{}:RCONtrol:RRC:STARt RRELease'
+    # require cell number
+    SCPI_CREATE_DEDICATED_BEARER = 'BSE:FUNCtion:LTE:{}:NAS:EBID10:DEDicated:CREate'
+    SCPI_CHANGE_SIM_NR_CMD = 'BSE:CONFig:NR5G:CELL1:SECurity:AUTHenticate:KEY:TYPE {}'
+    SCPI_CHANGE_SIM_LTE_CMD = 'BSE:CONFig:LTE:SECurity:AUTHenticate:KEY {}'
+    SCPI_SETTINGS_PRESET_CMD = 'SYSTem:PRESet:FULL'
+
+    # UXM's Test Application recovery
+    TA_BOOT_TIME = 100
+
+    # shh command
+    SSH_START_GUI_APP_CMD_FORMAT = 'psexec -s -d -i 1 "{exe_path}"'
+    SSH_CHECK_APP_RUNNING_CMD_FORMAT = 'tasklist | findstr /R {regex_app_name}'
+    SSH_KILL_PROCESS_BY_NAME = 'taskkill /IM {process_name} /F'
+    UXM_TEST_APP_NAME = 'TestApp.exe'
+
+    # start process success regex
+    PSEXEC_PROC_STARTED_REGEX_FORMAT = 'started on * with process ID {proc_id}'
+
+    # HCCU default value
+    HCCU_SOCKET_PORT = 4882
+    # number of digit of the length of setup name
+    HCCU_SCPI_CHANGE_SETUP_CMD = ':SYSTem:SETup:CONFig #{number_of_digit}{setup_name_len}{setup_name}'
+    HCCU_SCPI_CHANGE_SCENARIO_CMD = ':SETup:SCENe "((NE_1, {scenario_name}))"'
+    HCCU_STATUS_CHECK_CMD = ':SETup:INSTrument:STATus? 0\n'
+    HCCU_FR2_SETUP_NAME = '{Name:"TSPC_1UXM5G_HF_2RRH_M1740A"}'
+    HCCU_FR1_SETUP_NAME = '{Name:"TSPC_1UXM5G_LF"}'
+    HCCU_GET_INSTRUMENT_COUNT_CMD = ':SETup:INSTrument:COUNt?'
+    HCCU_FR2_INSTRUMENT_COUNT = 5
+    HCCU_FR1_INSTRUMENT_COUNT = 2
+    HCCU_FR2_SCENARIO = 'NR_4DL2x2_2UL2x2_LTE_4CC'
+    HCCU_FR1_SCENARIO = 'NR_1DL4x4_1UL2x2_LTE_4CC'
+
+
+    def __init__(self, ip_address, custom_files,uxm_user,
+                 ssh_private_key_to_uxm, ta_exe_path, ta_exe_name):
+        """Initializes the cellular simulator.
+
+        Args:
+            ip_address: the ip address of host where Keysight Test Application (TA)
+                is installed.
+            custom_files: a list of file path for custom files.
+            uxm_user: username of host where Keysight TA resides.
+            ssh_private_key_to_uxm: private key for key based ssh to
+                host where Keysight TA resides.
+            ta_exe_path: path to TA exe.
+            ta_exe_name: name of TA exe.
+        """
+        super().__init__()
+        self.custom_files = custom_files
+        self.rockbottom_script = None
+        self.cells = []
+        self.uxm_ip = ip_address
+        self.uxm_user = uxm_user
+        self.ssh_private_key_to_uxm = os.path.expanduser(
+                                        ssh_private_key_to_uxm)
+        self.ta_exe_path = ta_exe_path
+        self.ta_exe_name = ta_exe_name
+        self.ssh_client = self.create_ssh_client()
+
+        # get roclbottom file
+        for file in self.custom_files:
+            if 'rockbottom_' in file:
+                self.rockbottom_script = file
+
+        # connect to Keysight Test Application via socket
+        self.recovery_ta()
+        self.socket = self._socket_connect(self.uxm_ip, self.UXM_SOCKET_PORT)
+        self.check_socket_connection()
+        self.timeout = 120
+
+        # hccu socket
+        self.hccu_socket_port = self.HCCU_SOCKET_PORT
+        self.hccu_socket = SocketWrapper(self.uxm_ip, self.hccu_socket_port)
+
+    def socket_connect(self):
+        self.socket = self._socket_connect(self.uxm_ip, self.UXM_SOCKET_PORT)
+
+    def switch_HCCU_scenario(self, scenario_name: str):
+        cmd = self.HCCU_SCPI_CHANGE_SCENARIO_CMD.format(
+            scenario_name=scenario_name)
+        self.hccu_socket.send_command(cmd)
+        self.log.debug(f'Sent command: {cmd}')
+        # this is require for the command to take effect
+        # because hccu's port need to be free.
+        self.hccu_socket.close()
+
+    def switch_HCCU_setup(self, setup_name: str):
+        """Change HHCU system setup.
+
+        Args:
+            setup_name: a string name
+                of the system setup will be changed to.
+        """
+        setup_name_len = str(len(setup_name))
+        number_of_digit = str(len(setup_name_len))
+        cmd = self.HCCU_SCPI_CHANGE_SETUP_CMD.format(
+            number_of_digit=number_of_digit,
+            setup_name_len=setup_name_len,
+            setup_name=setup_name
+        )
+        self.hccu_socket.send_command(cmd)
+        self.log.debug(f'Sent command: {cmd}')
+        # this is require for the command to take effect
+        # because hccu's port need to be free.
+        self.hccu_socket.close()
+
+    def wait_until_hccu_operational(self, timeout=1200):
+        """ Wait for hccu is ready to operate for a specified timeout.
+
+        Args:
+            timeout: time we are waiting for
+                hccu in opertional status.
+
+        Returns:
+            True if HCCU status is operational within timeout.
+            False otherwise.
+        """
+        # check status
+        self.log.info('Waiting for HCCU to ready to operate.')
+        cmd = self.HCCU_STATUS_CHECK_CMD
+        t = 0
+        interval = 10
+        while t < timeout:
+            response = self.hccu_socket.send_command_recv(cmd)
+            if response == 'OPER\n':
+                return True
+            time.sleep(interval)
+            t += interval
+        return False
+
+    def switch_HCCU_settings(self, is_fr2: bool):
+        """Set HCCU setup configuration.
+
+        HCCU stands for Hardware Configuration Control Utility,
+        an interface allows us to control Keysight Test Equipment.
+
+        Args:
+            is_fr2: a bool value.
+        """
+        # change HCCU configration
+        data = ''
+        scenario_name = ''
+        instrument_count_res = self.hccu_socket.send_command_recv(
+            self.HCCU_GET_INSTRUMENT_COUNT_CMD)
+        instrument_count = int(instrument_count_res)
+        # if hccu setup is correct, no need to change.
+        if is_fr2 and instrument_count == self.HCCU_FR2_INSTRUMENT_COUNT:
+            self.log.info('UXM has correct HCCU setup.')
+            return
+        if not is_fr2 and instrument_count == self.HCCU_FR1_INSTRUMENT_COUNT:
+            self.log.info('UXM has correct HCCU setup.')
+            return
+
+        self.log.info('UXM has incorrect HCCU setup, start changing setup.')
+        # terminate TA and close socket
+        self.log.info('Terminate TA before switch HCCU settings.')
+        self.terminate_process(self.UXM_TEST_APP_NAME)
+        self.socket.close()
+
+        # change hccu setup
+        if is_fr2:
+            data = self.HCCU_FR2_SETUP_NAME
+            scenario_name = self.HCCU_FR2_SCENARIO
+        else:
+            data = self.HCCU_FR1_SETUP_NAME
+            scenario_name = self.HCCU_FR1_SCENARIO
+        self.log.info('Switch HCCU setup.')
+        self.switch_HCCU_setup(data)
+        time.sleep(10)
+        if not self.wait_until_hccu_operational():
+            raise RuntimeError('Fail to switch HCCU setup.')
+
+        # change scenario
+        self.log.info('Ativate HCCU scenario.')
+        self.switch_HCCU_scenario(scenario_name)
+        time.sleep(40)
+        if not self.wait_until_hccu_operational():
+            raise RuntimeError('Fail to switch HCCU scenario.')
+
+        # start TA and reconnect socket.
+        self.recovery_ta()
+        self.socket = self._socket_connect(self.uxm_ip, self.UXM_SOCKET_PORT)
+
+    def create_ssh_client(self):
+        """Create a ssh client to host."""
+        ssh = paramiko.SSHClient()
+        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+        mykey = paramiko.Ed25519Key.from_private_key_file(
+            self.ssh_private_key_to_uxm)
+        ssh.connect(hostname=self.uxm_ip, username=self.uxm_user, pkey=mykey)
+        self.log.info('SSH client to %s is connected' % self.uxm_ip)
+        return ssh
+
+    def terminate_process(self, process_name):
+        cmd = self.SSH_KILL_PROCESS_BY_NAME.format(
+            process_name=process_name
+        )
+        stdin, stdout, stderr = self.ssh_client.exec_command(cmd)
+        stdin.close()
+        err = ''.join(stderr.readlines())
+        out = ''.join(stdout.readlines())
+        final_output = str(out) + str(err)
+        self.log.info(final_output)
+        return out
+
+    def is_ta_running(self):
+        is_running_cmd = self.SSH_CHECK_APP_RUNNING_CMD_FORMAT.format(
+            regex_app_name=self.ta_exe_name)
+        stdin, stdout, stderr = self.ssh_client.exec_command(is_running_cmd)
+        stdin.close()
+        err = ''.join(stderr.readlines())
+        out = ''.join(stdout.readlines())
+        final_output = str(out) + str(err)
+        self.log.info(final_output)
+        return (out != '' and err == '')
+
+    def _start_test_app(self):
+        """Start Test Application on Windows."""
+        # start GUI exe via ssh
+        start_app_cmd = self.SSH_START_GUI_APP_CMD_FORMAT.format(
+            exe_path=self.ta_exe_path)
+        stdin, stdout, stderr = self.ssh_client.exec_command(start_app_cmd)
+        self.log.info(f'Command sent to {self.uxm_ip}: {start_app_cmd}')
+        stdin.close()
+        err = ''.join(stderr.readlines())
+        out = ''.join(stdout.readlines())
+        # psexec return process ID as part of the exit code
+        exit_status = stderr.channel.recv_exit_status()
+        is_started = re.search(
+            self.PSEXEC_PROC_STARTED_REGEX_FORMAT.format(proc_id=exit_status),
+            err[-1])
+        if is_started:
+            raise RuntimeError('Fail to start TA: ' + out + err)
+        # wait for ta completely boot up
+        self.log.info('TA is starting')
+        time.sleep(self.TA_BOOT_TIME)
+
+    def recovery_ta(self):
+        """Start TA if it is not running."""
+        if not self.is_ta_running():
+            self._start_test_app()
+            # checking if ta booting process complete
+            # by checking socket connection
+            s = None
+            retries = 12
+            for _ in range(retries):
+                try:
+                    s = self._socket_connect(self.uxm_ip, self.UXM_SOCKET_PORT)
+                    s.close()
+                    return
+                except ConnectionRefusedError as cre:
+                    self.log.info(
+                        'Connection refused, wait 10s for TA to boot')
+                    time.sleep(10)
+            raise RuntimeError('TA does not start on time')
+
+    def set_rockbottom_script_path(self, path):
+        """Set path to rockbottom script.
+
+        Args:
+            path: path to rockbottom script.
+        """
+        self.rockbottom_script = path
+
+    def set_cell_info(self, cell_info):
+        """Set type and number for multiple cells.
+
+        Args:
+            cell_info: list of dictionaries,
+                each dictionary contain cell type
+                and cell number for each cell
+                that the simulator need to control.
+        """
+        if not cell_info:
+            raise ValueError('Missing cell info from configurations file')
+        self.cells = cell_info
+
+    def deregister_ue_ims(self):
+        """Remove UE IMS profile from UXM."""
+        self._socket_send_SCPI_command(
+                self.SCPI_DEREGISTER_UE_IMS)
+
+    def create_dedicated_bearer(self):
+        """Create a dedicated bearer setup for ims call.
+
+        After UE connected and register on UXM IMS tab.
+        It is required to create a dedicated bearer setup
+        with EPS bearer ID 10.
+        """
+        cell_number = self.cells[0][self.KEY_CELL_NUMBER]
+        self._socket_send_SCPI_command(
+                self.SCPI_CREATE_DEDICATED_BEARER.format(cell_number))
+
+    def turn_cell_on(self, cell_type, cell_number):
+        """Turn UXM's cell on.
+
+        Args:
+            cell_type: type of cell (e.g NR5G, LTE).
+            cell_number: ordinal number of a cell.
+        """
+        if cell_type and cell_number:
+            self._socket_send_SCPI_command(
+                self.SCPI_CELL_ON_CMD.format(cell_type, cell_number))
+        else:
+            raise ValueError('Invalid cell info\n' +
+                             f' cell type: {cell_type}\n' +
+                             f' cell number: {cell_number}\n')
+
+    def turn_cell_off(self, cell_type, cell_number):
+        """Turn UXM's cell off.
+
+        Args:
+            cell_type: type of cell (e.g NR5G, LTE).
+            cell_number: ordinal number of a cell.
+        """
+        if cell_type and cell_number:
+            self._socket_send_SCPI_command(
+                self.SCPI_CELL_OFF_CMD.format(cell_type, cell_number))
+        else:
+            raise ValueError('Invalid cell info\n' +
+                             f' cell type: {cell_type}\n' +
+                             f' cell number: {cell_number}\n')
+
+    def get_cell_status(self, cell_type, cell_number):
+        """Get status of cell.
+
+        Args:
+            cell_type: type of cell (e.g NR5G, LTE).
+            cell_number: ordinal number of a cell.
+        """
+        if not cell_type or not cell_number:
+            raise ValueError('Invalid cell with\n' +
+                             f' cell type: {cell_type}\n' +
+                             f' cell number: {cell_number}\n')
+
+        return self._socket_send_SCPI_for_result_command(
+            self.SCPI_GET_CELL_STATUS.format(cell_type, cell_number))
+
+    def check_socket_connection(self):
+        """Check if the socket connection is established.
+
+        Query the identification of the Keysight Test Application
+        we are trying to connect to. Empty response indicates
+        connection fail, and vice versa.
+        """
+        self.socket.sendall(self.SCPI_CHECK_CONNECTION_CMD.encode())
+        response = self.socket.recv(1024).decode()
+        if response:
+            self.log.info(f'Connected to: {response}')
+        else:
+            self.log.error('Fail to connect to callbox')
+
+    def _socket_connect(self, host, port):
+        """Create socket connection.
+
+        Args:
+            host: IP address of desktop where Keysight Test Application resides.
+            port: port that Keysight Test Application is listening for socket
+                communication.
+        Returns:
+            s: socket object.
+        """
+        self.log.info('Establishing connection to callbox via socket')
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s.connect((host, port))
+        return s
+
+    def _socket_send_SCPI_command(self, command):
+        """Send SCPI command without expecting response.
+
+        Args:
+            command: a string SCPI command.
+        """
+        # make sure there is a line break for the socket to send command
+        command = command + '\n'
+        # send command
+        self.socket.sendall(command.encode())
+        self.log.info(f'Sent {command}')
+
+    def _socket_receive_SCPI_result(self):
+        """Receive response from socket. """
+        i = 1
+        response = ''
+        while i < self.timeout and not response:
+            response = self.socket.recv(1024).decode()
+            i += 1
+        return response
+
+    def _socket_send_SCPI_for_result_command(self, command):
+        """Send SCPI command and expecting response.
+
+        Args:
+            command: a string SCPI command.
+        """
+        self._socket_send_SCPI_command(command)
+        response = self._socket_receive_SCPI_result()
+        return response
+
+    def check_system_error(self):
+        """Query system error from Keysight Test Application.
+
+        Returns:
+            status: a message indicate the number of errors
+                and detail of errors if any.
+                a string `0,"No error"` indicates no error.
+        """
+        status = self._socket_send_SCPI_for_result_command(
+            self.SCPI_SYSTEM_ERROR_CHECK_CMD)
+        self.log.info(f'System error status: {status}')
+        return status
+
+    def import_configuration(self, path):
+        """Import SCPI config file.
+
+        Args:
+            path: path to SCPI file.
+        """
+        self._socket_send_SCPI_command(
+            self.SCPI_SETTINGS_PRESET_CMD)
+        time.sleep(10)
+        self._socket_send_SCPI_command(
+            self.SCPI_IMPORT_SCPI_FILE_CMD.format(path))
+        time.sleep(45)
+
+    def destroy(self):
+        """Close socket connection with UXM. """
+        self.socket.close()
+
+    def setup_lte_scenario(self, path):
+        """Configures the equipment for an LTE simulation.
+
+        Args:
+            path: path to SCPI config file.
+        """
+        self.import_configuration(path)
+
+    def dut_rockbottom(self, dut):
+        """Set the dut to rockbottom state.
+
+        Args:
+            dut: a CellularAndroid controller.
+        """
+        # The rockbottom script might include a device reboot, so it is
+        # necessary to stop SL4A during its execution.
+        dut.ad.stop_services()
+        self.log.info('Executing rockbottom script for ' + dut.ad.model)
+        os.chmod(self.rockbottom_script, 0o777)
+        os.system('{} {}'.format(self.rockbottom_script, dut.ad.serial))
+        # Make sure the DUT is in root mode after coming back
+        dut.ad.root_adb()
+        # Restart SL4A
+        dut.ad.start_services()
+
+    def set_sim_type(self, is_3gpp_sim):
+        sim_type = 'KEYSight'
+        if is_3gpp_sim:
+            sim_type = 'TEST3GPP'
+        self._socket_send_SCPI_command(
+            self.SCPI_CHANGE_SIM_NR_CMD.format(sim_type))
+        time.sleep(2)
+        self._socket_send_SCPI_command(
+            self.SCPI_CHANGE_SIM_LTE_CMD.format(sim_type))
+        time.sleep(2)
+
+    def wait_until_attached_one_cell(self,
+                                     cell_type,
+                                     cell_number,
+                                     dut,
+                                     wait_for_camp_interval,
+                                     attach_retries):
+        """Wait until connect to given UXM cell.
+
+        After turn off airplane mode, sleep for
+        wait_for_camp_interval seconds for device to camp.
+        If not device is not connected after the wait,
+        either toggle airplane mode on/off or reboot device.
+        Args:
+            cell_type: type of cell
+                which we are trying to connect to.
+            cell_number: ordinal number of a cell
+                which we are trying to connect to.
+            dut: a AndroidCellular controller.
+            wait_for_camp_interval: sleep interval,
+                wait for device to camp.
+            attach_retries: number of retry
+                to wait for device
+                to connect to 1 basestation.
+        Raise:
+            RuntimeError: device unable to connect to cell.
+        """
+        # airplane mode on
+        dut.toggle_airplane_mode(True)
+        time.sleep(5)
+
+        # turn cell on
+        self.turn_cell_on(cell_type, cell_number)
+        time.sleep(5)
+
+        interval = 10
+        # waits for device to camp
+        for index in range(1, attach_retries):
+            count = 0
+            # airplane mode off
+            dut.toggle_airplane_mode(False)
+            time.sleep(5)
+            # check connection in small interval
+            while count < wait_for_camp_interval:
+                time.sleep(interval)
+                cell_state = self.get_cell_status(cell_type, cell_number)
+                self.log.info(f'cell state: {cell_state}')
+                if cell_state == 'CONN\n':
+                    # wait for connection stable
+                    time.sleep(15)
+                    # check connection status again
+                    cell_state = self.get_cell_status(cell_type, cell_number)
+                    self.log.info(f'cell state: {cell_state}')
+                    if cell_state == 'CONN\n':
+                        return True
+                if cell_state == 'OFF\n':
+                    self.turn_cell_on(cell_type, cell_number)
+                    time.sleep(5)
+                count += interval
+
+            # reboot device
+            if (index % 2) == 0:
+                dut.ad.reboot()
+                if self.rockbottom_script:
+                    self.dut_rockbottom(dut)
+                else:
+                    self.log.warning(
+                        f'Rockbottom script was not executed after reboot.'
+                    )
+            # toggle APM and cell on/off
+            elif (index % 1) == 0:
+                # Toggle APM on
+                dut.toggle_airplane_mode(True)
+                time.sleep(5)
+
+                # Toggle simulator cell
+                self.turn_cell_off(cell_type, cell_number)
+                time.sleep(5)
+                self.turn_cell_on(cell_type, cell_number)
+                time.sleep(5)
+
+                # Toggle APM off
+                dut.toggle_airplane_mode(False)
+                time.sleep(5)
+            # increase length of small waiting interval
+            interval += 5
+
+        # Phone cannot connected to basestation of callbox
+        raise RuntimeError(
+            f'Phone was unable to connect to cell: {cell_type}-{cell_number}')
+
+    def wait_until_attached(self, dut, timeout, attach_retries):
+        """Waits until the DUT is attached to all required cells.
+
+        Args:
+            dut: a CellularAndroid controller.
+            timeout: sleep interval,
+                wait for device to camp in 1 try.
+            attach_retries: number of retry
+                to wait for device
+                to connect to 1 basestation.
+        """
+        # get cell info
+        first_cell_type = self.cells[0][self.KEY_CELL_TYPE]
+        first_cell_number = self.cells[0][self.KEY_CELL_NUMBER]
+        if len(self.cells) == 2:
+            second_cell_type = self.cells[1][self.KEY_CELL_TYPE]
+            second_cell_number = self.cells[1][self.KEY_CELL_NUMBER]
+
+        # connect to 1st cell
+        self.wait_until_attached_one_cell(first_cell_type,
+                                          first_cell_number, dut, timeout,
+                                          attach_retries)
+
+        # aggregation to NR
+        if len(self.cells) == 2:
+            self.turn_cell_on(
+                second_cell_type,
+                second_cell_number,
+            )
+
+            for _ in range(1, attach_retries):
+                self.log.info('Try to aggregate to NR.')
+                self._socket_send_SCPI_command(
+                    'BSE:CONFig:LTE:CELL1:CAGGregation:AGGRegate:NRCC:DL None')
+                self._socket_send_SCPI_command(
+                    'BSE:CONFig:LTE:CELL1:CAGGregation:AGGRegate:NRCC:UL None')
+                self._socket_send_SCPI_command(
+                    'BSE:CONFig:LTE:CELL1:CAGGregation:AGGRegate:NRCC:DL CELL1')
+                self._socket_send_SCPI_command(
+                    'BSE:CONFig:LTE:CELL1:CAGGregation:AGGRegate:NRCC:DL CELL1')
+                time.sleep(1)
+                self._socket_send_SCPI_command(
+                    "BSE:CONFig:LTE:CELL1:CAGGregation:AGGRegate:NRCC:APPly")
+                # wait for status stable
+                time.sleep(10)
+                cell_state = self.get_cell_status(second_cell_type, second_cell_number)
+                self.log.info(f'cell state: {cell_state}')
+                if cell_state == 'CONN\n':
+                    return
+                else:
+                    self.turn_cell_off(second_cell_type, second_cell_number)
+                    # wait for LTE cell to connect again
+                    self.wait_until_attached_one_cell(first_cell_type,
+                                            first_cell_number, dut, 120,
+                                            2)
+
+            raise RuntimeError(f'Fail to aggregate to NR from LTE.')
+
+    def set_lte_rrc_state_change_timer(self, enabled, time=10):
+        """Configures the LTE RRC state change timer.
+
+        Args:
+            enabled: a boolean indicating if the timer should be on or off.
+            time: time in seconds for the timer to expire.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_band(self, bts_index, band):
+        """Sets the band for the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            band: the new band.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def get_duplex_mode(self, band):
+        """Determines if the band uses FDD or TDD duplex mode
+
+        Args:
+            band: a band number.
+
+        Returns:
+            an variable of class DuplexMode indicating if band is FDD or TDD.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_input_power(self, bts_index, input_power):
+        """Sets the input power for the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            input_power: the new input power.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_output_power(self, bts_index, output_power):
+        """Sets the output power for the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            output_power: the new output power.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_tdd_config(self, bts_index, tdd_config):
+        """Sets the tdd configuration number for the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            tdd_config: the new tdd configuration number.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_ssf_config(self, bts_index, ssf_config):
+        """Sets the Special Sub-Frame config number for the indicated.
+
+        base station.
+
+        Args:
+            bts_index: the base station number.
+            ssf_config: the new ssf config number.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_bandwidth(self, bts_index, bandwidth):
+        """Sets the bandwidth for the indicated base station.
+
+        Args:
+            bts_index: the base station number
+            bandwidth: the new bandwidth
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_downlink_channel_number(self, bts_index, channel_number):
+        """Sets the downlink channel number for the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            channel_number: the new channel number.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_mimo_mode(self, bts_index, mimo_mode):
+        """Sets the mimo mode for the indicated base station.
+
+        Args:
+            bts_index: the base station number
+            mimo_mode: the new mimo mode
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_transmission_mode(self, bts_index, tmode):
+        """Sets the transmission mode for the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            tmode: the new transmission mode.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_scheduling_mode(self,
+                            bts_index,
+                            scheduling,
+                            mcs_dl=None,
+                            mcs_ul=None,
+                            nrb_dl=None,
+                            nrb_ul=None):
+        """Sets the scheduling mode for the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            scheduling: the new scheduling mode.
+            mcs_dl: Downlink MCS.
+            mcs_ul: Uplink MCS.
+            nrb_dl: Number of RBs for downlink.
+            nrb_ul: Number of RBs for uplink.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_dl_256_qam_enabled(self, bts_index, enabled):
+        """Determines what MCS table should be used for the downlink.
+
+        This only saves the setting that will be used when configuring MCS.
+
+        Args:
+            bts_index: the base station number.
+            enabled: whether 256 QAM should be used.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_ul_64_qam_enabled(self, bts_index, enabled):
+        """Determines what MCS table should be used for the uplink.
+
+        This only saves the setting that will be used when configuring MCS.
+
+        Args:
+            bts_index: the base station number.
+            enabled: whether 64 QAM should be used.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_mac_padding(self, bts_index, mac_padding):
+        """Enables or disables MAC padding in the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            mac_padding: the new MAC padding setting.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_cfi(self, bts_index, cfi):
+        """Sets the Channel Format Indicator for the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            cfi: the new CFI setting.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_paging_cycle(self, bts_index, cycle_duration):
+        """Sets the paging cycle duration for the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            cycle_duration: the new paging cycle duration in milliseconds.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def set_phich_resource(self, bts_index, phich):
+        """Sets the PHICH Resource setting for the indicated base station.
+
+        Args:
+            bts_index: the base station number.
+            phich: the new PHICH resource setting.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def lte_attach_secondary_carriers(self, ue_capability_enquiry):
+        """Activates the secondary carriers for CA.
+
+        Requires the DUT to be attached to the primary carrier first.
+
+        Args:
+            ue_capability_enquiry: UE capability enquiry message to be sent to
+              the UE before starting carrier aggregation.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def wait_until_communication_state(self, timeout=120):
+        """Waits until the DUT is in Communication state.
+
+        Args:
+            timeout: after this amount of time the method will raise
+                a CellularSimulatorError exception. Default is 120 seconds.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def wait_until_idle_state(self, timeout=120):
+        """Waits until the DUT is in Idle state.
+
+        Args:
+            timeout: after this amount of time the method will raise a
+                CellularSimulatorError exception. Default is 120 seconds.
+        """
+        # turn on RRC release
+        cell_type = self.cells[0][self.KEY_CELL_TYPE]
+        cell_number = self.cells[0][self.KEY_CELL_NUMBER]
+
+        # choose cmd base on cell type
+        cmd = None
+        if cell_type == 'LTE':
+            cmd = self.SCPI_RRC_RELEASE_LTE_CMD
+        else:
+            cmd = self.SCPI_RRC_RELEASE_NR_CMD
+
+        if not cmd:
+            raise RuntimeError(f'Cell type [{cell_type}] is not supporting IDLE.')
+
+        # checking status
+        self.log.info('Wait for IDLE state.')
+        for _ in range(5):
+            cell_state = self.get_cell_status(cell_type, cell_number)
+            self.log.info(f'cell state: {cell_state}')
+            if cell_state == 'CONN\n':
+                # RRC release
+                self._socket_send_SCPI_command(cmd.format(cell_type, cell_number))
+                # wait for status stable
+                time.sleep(60)
+            elif cell_state == 'IDLE\n':
+                return
+
+        raise RuntimeError('RRC release fail.')
+
+    def detach(self):
+        """ Turns off all the base stations so the DUT loose connection."""
+        for cell in self.cells:
+            cell_type = cell[self.KEY_CELL_TYPE]
+            cell_number = cell[self.KEY_CELL_NUMBER]
+            self.turn_cell_off(cell_type, cell_number)
+            time.sleep(5)
+
+    def stop(self):
+        """Stops current simulation.
+
+        After calling this method, the simulator will need to be set up again.
+        """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def start_data_traffic(self):
+        """Starts transmitting data from the instrument to the DUT. """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
+
+    def stop_data_traffic(self):
+        """Stops transmitting data from the instrument to the DUT. """
+        raise NotImplementedError(
+            'This UXM callbox simulator does not support this feature.')
diff --git a/acts/framework/acts/libs/ota/ota_tools/ota_tool.py b/acts/framework/acts/libs/ota/ota_tools/ota_tool.py
index e7bb565..1525fc8 100644
--- a/acts/framework/acts/libs/ota/ota_tools/ota_tool.py
+++ b/acts/framework/acts/libs/ota/ota_tools/ota_tool.py
@@ -44,4 +44,3 @@
         Args:
             ota_runner: The OTA Runner that handles the device information.
         """
-        pass
diff --git a/acts/framework/acts/libs/ota/ota_tools/update_device_ota_tool.py b/acts/framework/acts/libs/ota/ota_tools/update_device_ota_tool.py
index 7d5d772..b7bf382 100644
--- a/acts/framework/acts/libs/ota/ota_tools/update_device_ota_tool.py
+++ b/acts/framework/acts/libs/ota/ota_tools/update_device_ota_tool.py
@@ -42,7 +42,7 @@
     def update(self, ota_runner):
         logging.info('Forcing adb to be in root mode.')
         ota_runner.android_device.root_adb()
-        update_command = 'python2.7 %s -s %s %s' % (
+        update_command = 'python3 %s -s %s %s' % (
             self.command, ota_runner.serial, ota_runner.get_ota_package())
         logging.info('Running %s' % update_command)
         result = job.run(update_command, timeout=UPDATE_TIMEOUT)
diff --git a/acts/framework/acts/libs/testtracker/protos/gen/testtracker_result_pb2.py b/acts/framework/acts/libs/testtracker/protos/gen/testtracker_result_pb2.py
index 97f576b..da94a7e 100644
--- a/acts/framework/acts/libs/testtracker/protos/gen/testtracker_result_pb2.py
+++ b/acts/framework/acts/libs/testtracker/protos/gen/testtracker_result_pb2.py
@@ -1,11 +1,10 @@
+# -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: testtracker_result.proto
-
-import sys
-_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -14,262 +13,17 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='testtracker_result.proto',
-  package='',
-  syntax='proto2',
-  serialized_options=None,
-  serialized_pb=_b('\n\x18testtracker_result.proto\"\xa8\x01\n\x08Property\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x16\n\x0cstring_value\x18\x65 \x01(\tH\x00\x12\x13\n\tint_value\x18\x66 \x01(\x03H\x00\x12\x16\n\x0c\x64ouble_value\x18g \x01(\x01H\x00\x12\x14\n\njson_value\x18h \x01(\tH\x00\x12\x14\n\nbool_value\x18i \x01(\x08H\x00\x12\x14\n\nbyte_value\x18j \x01(\x0cH\x00\x42\x07\n\x05value\"\xcc\x02\n\x06Result\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04uuid\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0e\n\x06\x64\x65tail\x18\x04 \x01(\t\x12\x1b\n\x08property\x18\x05 \x03(\x0b\x32\t.Property\x12\x11\n\ttimestamp\x18\n \x01(\t\x12.\n\x06status\x18\r \x01(\x0e\x32\x0e.Result.Status:\x0eSTATUS_UNKNOWN\"\xa0\x01\n\x06Status\x12\x12\n\x0eSTATUS_UNKNOWN\x10\x00\x12\n\n\x06PASSED\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x12\t\n\x05\x45RROR\x10\x03\x12\x0f\n\x0bINTERRUPTED\x10\x04\x12\r\n\tCANCELLED\x10\x05\x12\x0c\n\x08\x46ILTERED\x10\x06\x12\x0b\n\x07SKIPPED\x10\x07\x12\x0e\n\nSUPPRESSED\x10\x08\x12\x0b\n\x07\x42LOCKED\x10\t\x12\x07\n\x03TBR\x10\x11')
-)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18testtracker_result.proto\"\xa8\x01\n\x08Property\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x16\n\x0cstring_value\x18\x65 \x01(\tH\x00\x12\x13\n\tint_value\x18\x66 \x01(\x03H\x00\x12\x16\n\x0c\x64ouble_value\x18g \x01(\x01H\x00\x12\x14\n\njson_value\x18h \x01(\tH\x00\x12\x14\n\nbool_value\x18i \x01(\x08H\x00\x12\x14\n\nbyte_value\x18j \x01(\x0cH\x00\x42\x07\n\x05value\"\xcc\x02\n\x06Result\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04uuid\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0e\n\x06\x64\x65tail\x18\x04 \x01(\t\x12\x1b\n\x08property\x18\x05 \x03(\x0b\x32\t.Property\x12\x11\n\ttimestamp\x18\n \x01(\t\x12.\n\x06status\x18\r \x01(\x0e\x32\x0e.Result.Status:\x0eSTATUS_UNKNOWN\"\xa0\x01\n\x06Status\x12\x12\n\x0eSTATUS_UNKNOWN\x10\x00\x12\n\n\x06PASSED\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x12\t\n\x05\x45RROR\x10\x03\x12\x0f\n\x0bINTERRUPTED\x10\x04\x12\r\n\tCANCELLED\x10\x05\x12\x0c\n\x08\x46ILTERED\x10\x06\x12\x0b\n\x07SKIPPED\x10\x07\x12\x0e\n\nSUPPRESSED\x10\x08\x12\x0b\n\x07\x42LOCKED\x10\t\x12\x07\n\x03TBR\x10\x11')
 
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'testtracker_result_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-
-_RESULT_STATUS = _descriptor.EnumDescriptor(
-  name='Status',
-  full_name='Result.Status',
-  filename=None,
-  file=DESCRIPTOR,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='STATUS_UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='PASSED', index=1, number=1,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='FAILED', index=2, number=2,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='ERROR', index=3, number=3,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='INTERRUPTED', index=4, number=4,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='CANCELLED', index=5, number=5,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='FILTERED', index=6, number=6,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='SKIPPED', index=7, number=7,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='SUPPRESSED', index=8, number=8,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='BLOCKED', index=9, number=9,
-      serialized_options=None,
-      type=None),
-    _descriptor.EnumValueDescriptor(
-      name='TBR', index=10, number=17,
-      serialized_options=None,
-      type=None),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=372,
-  serialized_end=532,
-)
-_sym_db.RegisterEnumDescriptor(_RESULT_STATUS)
-
-
-_PROPERTY = _descriptor.Descriptor(
-  name='Property',
-  full_name='Property',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='Property.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='string_value', full_name='Property.string_value', index=1,
-      number=101, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='int_value', full_name='Property.int_value', index=2,
-      number=102, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='double_value', full_name='Property.double_value', index=3,
-      number=103, type=1, cpp_type=5, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='json_value', full_name='Property.json_value', index=4,
-      number=104, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='bool_value', full_name='Property.bool_value', index=5,
-      number=105, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='byte_value', full_name='Property.byte_value', index=6,
-      number=106, type=12, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b(""),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-    _descriptor.OneofDescriptor(
-      name='value', full_name='Property.value',
-      index=0, containing_type=None, fields=[]),
-  ],
-  serialized_start=29,
-  serialized_end=197,
-)
-
-
-_RESULT = _descriptor.Descriptor(
-  name='Result',
-  full_name='Result',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='name', full_name='Result.name', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='uuid', full_name='Result.uuid', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='description', full_name='Result.description', index=2,
-      number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='detail', full_name='Result.detail', index=3,
-      number=4, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='property', full_name='Result.property', index=4,
-      number=5, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='timestamp', full_name='Result.timestamp', index=5,
-      number=10, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='status', full_name='Result.status', index=6,
-      number=13, type=14, cpp_type=8, label=1,
-      has_default_value=True, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _RESULT_STATUS,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=200,
-  serialized_end=532,
-)
-
-_PROPERTY.oneofs_by_name['value'].fields.append(
-  _PROPERTY.fields_by_name['string_value'])
-_PROPERTY.fields_by_name['string_value'].containing_oneof = _PROPERTY.oneofs_by_name['value']
-_PROPERTY.oneofs_by_name['value'].fields.append(
-  _PROPERTY.fields_by_name['int_value'])
-_PROPERTY.fields_by_name['int_value'].containing_oneof = _PROPERTY.oneofs_by_name['value']
-_PROPERTY.oneofs_by_name['value'].fields.append(
-  _PROPERTY.fields_by_name['double_value'])
-_PROPERTY.fields_by_name['double_value'].containing_oneof = _PROPERTY.oneofs_by_name['value']
-_PROPERTY.oneofs_by_name['value'].fields.append(
-  _PROPERTY.fields_by_name['json_value'])
-_PROPERTY.fields_by_name['json_value'].containing_oneof = _PROPERTY.oneofs_by_name['value']
-_PROPERTY.oneofs_by_name['value'].fields.append(
-  _PROPERTY.fields_by_name['bool_value'])
-_PROPERTY.fields_by_name['bool_value'].containing_oneof = _PROPERTY.oneofs_by_name['value']
-_PROPERTY.oneofs_by_name['value'].fields.append(
-  _PROPERTY.fields_by_name['byte_value'])
-_PROPERTY.fields_by_name['byte_value'].containing_oneof = _PROPERTY.oneofs_by_name['value']
-_RESULT.fields_by_name['property'].message_type = _PROPERTY
-_RESULT.fields_by_name['status'].enum_type = _RESULT_STATUS
-_RESULT_STATUS.containing_type = _RESULT
-DESCRIPTOR.message_types_by_name['Property'] = _PROPERTY
-DESCRIPTOR.message_types_by_name['Result'] = _RESULT
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-Property = _reflection.GeneratedProtocolMessageType('Property', (_message.Message,), dict(
-  DESCRIPTOR = _PROPERTY,
-  __module__ = 'testtracker_result_pb2'
-  # @@protoc_insertion_point(class_scope:Property)
-  ))
-_sym_db.RegisterMessage(Property)
-
-Result = _reflection.GeneratedProtocolMessageType('Result', (_message.Message,), dict(
-  DESCRIPTOR = _RESULT,
-  __module__ = 'testtracker_result_pb2'
-  # @@protoc_insertion_point(class_scope:Result)
-  ))
-_sym_db.RegisterMessage(Result)
-
-
+  DESCRIPTOR._options = None
+  _PROPERTY._serialized_start=29
+  _PROPERTY._serialized_end=197
+  _RESULT._serialized_start=200
+  _RESULT._serialized_end=532
+  _RESULT_STATUS._serialized_start=372
+  _RESULT_STATUS._serialized_end=532
 # @@protoc_insertion_point(module_scope)
diff --git a/acts/framework/acts/libs/testtracker/testtracker_results_writer.py b/acts/framework/acts/libs/testtracker/testtracker_results_writer.py
index a1c4df0..7560fb5 100644
--- a/acts/framework/acts/libs/testtracker/testtracker_results_writer.py
+++ b/acts/framework/acts/libs/testtracker/testtracker_results_writer.py
@@ -44,7 +44,6 @@
 
 class TestTrackerError(Exception):
     """Exception class for errors raised within TestTrackerResultsWriter"""
-    pass
 
 
 class TestTrackerResultsWriter(object):
@@ -52,6 +51,7 @@
     writes it to the log directory. In automation, these protos will
     automatically be read from Sponge and uploaded to TestTracker.
     """
+
     def __init__(self, log_path, properties):
         """Creates a TestTrackerResultsWriter
 
@@ -104,7 +104,9 @@
             TestTrackerError if one or more required properties is absent
         """
         required_props = [KEY_USER, KEY_PROJECT_ID, KEY_EFFORT_NAME]
-        missing_props = [p for p in required_props if p not in self._properties]
+        missing_props = [
+            p for p in required_props if p not in self._properties
+        ]
         if missing_props:
             raise TestTrackerError(
                 'Missing the following required properties for TestTracker: %s'
@@ -143,11 +145,10 @@
         result = Result()
         result.uuid = uuid
         result.status = _TEST_RESULT_TO_STATUS_MAP[record.result]
-        result.timestamp = (
-            datetime.datetime.fromtimestamp(
-                record.begin_time / 1000, datetime.timezone.utc)
-            .isoformat(timespec='milliseconds')
-            .replace('+00:00', 'Z'))
+        result.timestamp = (datetime.datetime.fromtimestamp(
+            record.begin_time / 1000,
+            datetime.timezone.utc).isoformat(timespec='milliseconds').replace(
+                '+00:00', 'Z'))
 
         self._add_property(result, KEY_UUID, uuid)
         if record.details:
@@ -166,7 +167,8 @@
 
         Returns: Path to the created directory.
         """
-        dir_path = os.path.join(self._log_path, TESTTRACKER_PATH % (
-            self._properties[KEY_EFFORT_NAME], uuid))
+        dir_path = os.path.join(
+            self._log_path,
+            TESTTRACKER_PATH % (self._properties[KEY_EFFORT_NAME], uuid))
         os.makedirs(dir_path, exist_ok=True)
         return dir_path
diff --git a/acts/framework/acts/metrics/logger.py b/acts/framework/acts/metrics/logger.py
index 57cc9d8..e82bed2 100644
--- a/acts/framework/acts/metrics/logger.py
+++ b/acts/framework/acts/metrics/logger.py
@@ -146,7 +146,6 @@
         Args:
             event: the event that is triggering this start
         """
-        pass
 
     def end(self, event):
         """End the logging process.
@@ -154,7 +153,6 @@
         Args:
             event: the event that is triggering this start
         """
-        pass
 
     def _init_for_event(self, event):
         """Populate unset attributes with default values."""
@@ -223,7 +221,8 @@
         Args:
             event: The event that triggered this logger.
         """
-        self._logger = self._logger_cls(event=event, *self._logger_args,
+        self._logger = self._logger_cls(event=event,
+                                        *self._logger_args,
                                         **self._logger_kwargs)
         self._logger.start(event)
 
diff --git a/acts/framework/acts/metrics/loggers/protos/gen/acts_blackbox_pb2.py b/acts/framework/acts/metrics/loggers/protos/gen/acts_blackbox_pb2.py
index 03cce5d..2c69643 100644
--- a/acts/framework/acts/metrics/loggers/protos/gen/acts_blackbox_pb2.py
+++ b/acts/framework/acts/metrics/loggers/protos/gen/acts_blackbox_pb2.py
@@ -1,10 +1,10 @@
 # -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: acts_blackbox.proto
-
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -13,114 +13,16 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='acts_blackbox.proto',
-  package='acts.metrics.blackbox',
-  syntax='proto2',
-  serialized_options=b'\n!com.android.acts.metrics.blackbox',
-  create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x13\x61\x63ts_blackbox.proto\x12\x15\x61\x63ts.metrics.blackbox\"]\n\x18\x41\x63tsBlackboxMetricResult\x12\x17\n\x0ftest_identifier\x18\x01 \x01(\t\x12\x12\n\nmetric_key\x18\x03 \x02(\t\x12\x14\n\x0cmetric_value\x18\x04 \x02(\x01\"x\n\x1f\x41\x63tsBlackboxMetricResultsBundle\x12U\n\x1c\x61\x63ts_blackbox_metric_results\x18\x01 \x03(\x0b\x32/.acts.metrics.blackbox.ActsBlackboxMetricResultB#\n!com.android.acts.metrics.blackbox'
-)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13\x61\x63ts_blackbox.proto\x12\x15\x61\x63ts.metrics.blackbox\"]\n\x18\x41\x63tsBlackboxMetricResult\x12\x17\n\x0ftest_identifier\x18\x01 \x01(\t\x12\x12\n\nmetric_key\x18\x03 \x02(\t\x12\x14\n\x0cmetric_value\x18\x04 \x02(\x01\"x\n\x1f\x41\x63tsBlackboxMetricResultsBundle\x12U\n\x1c\x61\x63ts_blackbox_metric_results\x18\x01 \x03(\x0b\x32/.acts.metrics.blackbox.ActsBlackboxMetricResultB#\n!com.android.acts.metrics.blackbox')
 
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'acts_blackbox_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-
-
-_ACTSBLACKBOXMETRICRESULT = _descriptor.Descriptor(
-  name='ActsBlackboxMetricResult',
-  full_name='acts.metrics.blackbox.ActsBlackboxMetricResult',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='test_identifier', full_name='acts.metrics.blackbox.ActsBlackboxMetricResult.test_identifier', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='metric_key', full_name='acts.metrics.blackbox.ActsBlackboxMetricResult.metric_key', index=1,
-      number=3, type=9, cpp_type=9, label=2,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='metric_value', full_name='acts.metrics.blackbox.ActsBlackboxMetricResult.metric_value', index=2,
-      number=4, type=1, cpp_type=5, label=2,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=46,
-  serialized_end=139,
-)
-
-
-_ACTSBLACKBOXMETRICRESULTSBUNDLE = _descriptor.Descriptor(
-  name='ActsBlackboxMetricResultsBundle',
-  full_name='acts.metrics.blackbox.ActsBlackboxMetricResultsBundle',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='acts_blackbox_metric_results', full_name='acts.metrics.blackbox.ActsBlackboxMetricResultsBundle.acts_blackbox_metric_results', index=0,
-      number=1, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=141,
-  serialized_end=261,
-)
-
-_ACTSBLACKBOXMETRICRESULTSBUNDLE.fields_by_name['acts_blackbox_metric_results'].message_type = _ACTSBLACKBOXMETRICRESULT
-DESCRIPTOR.message_types_by_name['ActsBlackboxMetricResult'] = _ACTSBLACKBOXMETRICRESULT
-DESCRIPTOR.message_types_by_name['ActsBlackboxMetricResultsBundle'] = _ACTSBLACKBOXMETRICRESULTSBUNDLE
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-ActsBlackboxMetricResult = _reflection.GeneratedProtocolMessageType('ActsBlackboxMetricResult', (_message.Message,), {
-  'DESCRIPTOR' : _ACTSBLACKBOXMETRICRESULT,
-  '__module__' : 'acts_blackbox_pb2'
-  # @@protoc_insertion_point(class_scope:acts.metrics.blackbox.ActsBlackboxMetricResult)
-  })
-_sym_db.RegisterMessage(ActsBlackboxMetricResult)
-
-ActsBlackboxMetricResultsBundle = _reflection.GeneratedProtocolMessageType('ActsBlackboxMetricResultsBundle', (_message.Message,), {
-  'DESCRIPTOR' : _ACTSBLACKBOXMETRICRESULTSBUNDLE,
-  '__module__' : 'acts_blackbox_pb2'
-  # @@protoc_insertion_point(class_scope:acts.metrics.blackbox.ActsBlackboxMetricResultsBundle)
-  })
-_sym_db.RegisterMessage(ActsBlackboxMetricResultsBundle)
-
-
-DESCRIPTOR._options = None
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n!com.android.acts.metrics.blackbox'
+  _ACTSBLACKBOXMETRICRESULT._serialized_start=46
+  _ACTSBLACKBOXMETRICRESULT._serialized_end=139
+  _ACTSBLACKBOXMETRICRESULTSBUNDLE._serialized_start=141
+  _ACTSBLACKBOXMETRICRESULTSBUNDLE._serialized_end=261
 # @@protoc_insertion_point(module_scope)
diff --git a/acts/framework/acts/metrics/loggers/protos/gen/acts_usage_metadata_pb2.py b/acts/framework/acts/metrics/loggers/protos/gen/acts_usage_metadata_pb2.py
index 12a4715..b50a3fb 100644
--- a/acts/framework/acts/metrics/loggers/protos/gen/acts_usage_metadata_pb2.py
+++ b/acts/framework/acts/metrics/loggers/protos/gen/acts_usage_metadata_pb2.py
@@ -1,11 +1,10 @@
+# -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: acts_usage_metadata.proto
-
-import sys
-_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -14,111 +13,16 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='acts_usage_metadata.proto',
-  package='acts.metadata',
-  syntax='proto2',
-  serialized_options=_b('\n\031com.android.acts.metadata'),
-  serialized_pb=_b('\n\x19\x61\x63ts_usage_metadata.proto\x12\racts.metadata\"L\n\x13\x41\x63tsTestRunMetadata\x12\x35\n\x05usage\x18\x01 \x03(\x0b\x32&.acts.metadata.ActsMethodUsageMetadata\"Y\n\x17\x41\x63tsMethodUsageMetadata\x12\x14\n\x0ctest_context\x18\x01 \x01(\t\x12\x19\n\x11method_identifier\x18\x02 \x01(\t\x12\r\n\x05\x63ount\x18\x03 \x01(\x05\x42\x1b\n\x19\x63om.android.acts.metadata')
-)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x61\x63ts_usage_metadata.proto\x12\racts.metadata\"L\n\x13\x41\x63tsTestRunMetadata\x12\x35\n\x05usage\x18\x01 \x03(\x0b\x32&.acts.metadata.ActsMethodUsageMetadata\"Y\n\x17\x41\x63tsMethodUsageMetadata\x12\x14\n\x0ctest_context\x18\x01 \x01(\t\x12\x19\n\x11method_identifier\x18\x02 \x01(\t\x12\r\n\x05\x63ount\x18\x03 \x01(\x05\x42\x1b\n\x19\x63om.android.acts.metadata')
 
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'acts_usage_metadata_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-
-
-_ACTSTESTRUNMETADATA = _descriptor.Descriptor(
-  name='ActsTestRunMetadata',
-  full_name='acts.metadata.ActsTestRunMetadata',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='usage', full_name='acts.metadata.ActsTestRunMetadata.usage', index=0,
-      number=1, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=44,
-  serialized_end=120,
-)
-
-
-_ACTSMETHODUSAGEMETADATA = _descriptor.Descriptor(
-  name='ActsMethodUsageMetadata',
-  full_name='acts.metadata.ActsMethodUsageMetadata',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='test_context', full_name='acts.metadata.ActsMethodUsageMetadata.test_context', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='method_identifier', full_name='acts.metadata.ActsMethodUsageMetadata.method_identifier', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=_b("").decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-    _descriptor.FieldDescriptor(
-      name='count', full_name='acts.metadata.ActsMethodUsageMetadata.count', index=2,
-      number=3, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=122,
-  serialized_end=211,
-)
-
-_ACTSTESTRUNMETADATA.fields_by_name['usage'].message_type = _ACTSMETHODUSAGEMETADATA
-DESCRIPTOR.message_types_by_name['ActsTestRunMetadata'] = _ACTSTESTRUNMETADATA
-DESCRIPTOR.message_types_by_name['ActsMethodUsageMetadata'] = _ACTSMETHODUSAGEMETADATA
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-ActsTestRunMetadata = _reflection.GeneratedProtocolMessageType('ActsTestRunMetadata', (_message.Message,), dict(
-  DESCRIPTOR = _ACTSTESTRUNMETADATA,
-  __module__ = 'acts_usage_metadata_pb2'
-  # @@protoc_insertion_point(class_scope:acts.metadata.ActsTestRunMetadata)
-  ))
-_sym_db.RegisterMessage(ActsTestRunMetadata)
-
-ActsMethodUsageMetadata = _reflection.GeneratedProtocolMessageType('ActsMethodUsageMetadata', (_message.Message,), dict(
-  DESCRIPTOR = _ACTSMETHODUSAGEMETADATA,
-  __module__ = 'acts_usage_metadata_pb2'
-  # @@protoc_insertion_point(class_scope:acts.metadata.ActsMethodUsageMetadata)
-  ))
-_sym_db.RegisterMessage(ActsMethodUsageMetadata)
-
-
-DESCRIPTOR._options = None
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\031com.android.acts.metadata'
+  _ACTSTESTRUNMETADATA._serialized_start=44
+  _ACTSTESTRUNMETADATA._serialized_end=120
+  _ACTSMETHODUSAGEMETADATA._serialized_start=122
+  _ACTSMETHODUSAGEMETADATA._serialized_end=211
 # @@protoc_insertion_point(module_scope)
diff --git a/acts/framework/acts/metrics/loggers/protos/gen/metrics_pb2.py b/acts/framework/acts/metrics/loggers/protos/gen/metrics_pb2.py
index 9c8ef3b..bd88534 100644
--- a/acts/framework/acts/metrics/loggers/protos/gen/metrics_pb2.py
+++ b/acts/framework/acts/metrics/loggers/protos/gen/metrics_pb2.py
@@ -1,10 +1,10 @@
 # -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: metrics.proto
-
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -13,239 +13,20 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='metrics.proto',
-  package='acts.metrics',
-  syntax='proto2',
-  serialized_options=b'\n\030com.android.acts.metrics',
-  create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\rmetrics.proto\x12\x0c\x61\x63ts.metrics\"\xec\x01\n\rBoundedMetric\x12\x12\n\ntest_class\x18\x01 \x01(\t\x12\x13\n\x0btest_method\x18\x02 \x01(\t\x12\x0e\n\x06metric\x18\x03 \x01(\t\x12\r\n\x05value\x18\x04 \x01(\x01\x12.\n\x0blower_limit\x18\x05 \x01(\x0b\x32\x19.acts.metrics.DoubleValue\x12.\n\x0bupper_limit\x18\x06 \x01(\x0b\x32\x19.acts.metrics.DoubleValue\x12\x0c\n\x04unit\x18\x07 \x01(\t\x12%\n\x05\x65xtra\x18\x08 \x03(\x0b\x32\x16.acts.metrics.KeyValue\"\x1c\n\x0b\x44oubleValue\x12\r\n\x05value\x18\x01 \x01(\x01\"&\n\x08KeyValue\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"L\n\x14\x42oundedMetricsBundle\x12\x34\n\x0f\x62ounded_metrics\x18\x01 \x03(\x0b\x32\x1b.acts.metrics.BoundedMetricB\x1a\n\x18\x63om.android.acts.metrics'
-)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rmetrics.proto\x12\x0c\x61\x63ts.metrics\"\xec\x01\n\rBoundedMetric\x12\x12\n\ntest_class\x18\x01 \x01(\t\x12\x13\n\x0btest_method\x18\x02 \x01(\t\x12\x0e\n\x06metric\x18\x03 \x01(\t\x12\r\n\x05value\x18\x04 \x01(\x01\x12.\n\x0blower_limit\x18\x05 \x01(\x0b\x32\x19.acts.metrics.DoubleValue\x12.\n\x0bupper_limit\x18\x06 \x01(\x0b\x32\x19.acts.metrics.DoubleValue\x12\x0c\n\x04unit\x18\x07 \x01(\t\x12%\n\x05\x65xtra\x18\x08 \x03(\x0b\x32\x16.acts.metrics.KeyValue\"\x1c\n\x0b\x44oubleValue\x12\r\n\x05value\x18\x01 \x01(\x01\"&\n\x08KeyValue\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"L\n\x14\x42oundedMetricsBundle\x12\x34\n\x0f\x62ounded_metrics\x18\x01 \x03(\x0b\x32\x1b.acts.metrics.BoundedMetricB\x1a\n\x18\x63om.android.acts.metrics')
 
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'metrics_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-
-
-_BOUNDEDMETRIC = _descriptor.Descriptor(
-  name='BoundedMetric',
-  full_name='acts.metrics.BoundedMetric',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='test_class', full_name='acts.metrics.BoundedMetric.test_class', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='test_method', full_name='acts.metrics.BoundedMetric.test_method', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='metric', full_name='acts.metrics.BoundedMetric.metric', index=2,
-      number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='value', full_name='acts.metrics.BoundedMetric.value', index=3,
-      number=4, type=1, cpp_type=5, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='lower_limit', full_name='acts.metrics.BoundedMetric.lower_limit', index=4,
-      number=5, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='upper_limit', full_name='acts.metrics.BoundedMetric.upper_limit', index=5,
-      number=6, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='unit', full_name='acts.metrics.BoundedMetric.unit', index=6,
-      number=7, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='extra', full_name='acts.metrics.BoundedMetric.extra', index=7,
-      number=8, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=32,
-  serialized_end=268,
-)
-
-
-_DOUBLEVALUE = _descriptor.Descriptor(
-  name='DoubleValue',
-  full_name='acts.metrics.DoubleValue',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='value', full_name='acts.metrics.DoubleValue.value', index=0,
-      number=1, type=1, cpp_type=5, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=270,
-  serialized_end=298,
-)
-
-
-_KEYVALUE = _descriptor.Descriptor(
-  name='KeyValue',
-  full_name='acts.metrics.KeyValue',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='key', full_name='acts.metrics.KeyValue.key', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='value', full_name='acts.metrics.KeyValue.value', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=300,
-  serialized_end=338,
-)
-
-
-_BOUNDEDMETRICSBUNDLE = _descriptor.Descriptor(
-  name='BoundedMetricsBundle',
-  full_name='acts.metrics.BoundedMetricsBundle',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='bounded_metrics', full_name='acts.metrics.BoundedMetricsBundle.bounded_metrics', index=0,
-      number=1, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=340,
-  serialized_end=416,
-)
-
-_BOUNDEDMETRIC.fields_by_name['lower_limit'].message_type = _DOUBLEVALUE
-_BOUNDEDMETRIC.fields_by_name['upper_limit'].message_type = _DOUBLEVALUE
-_BOUNDEDMETRIC.fields_by_name['extra'].message_type = _KEYVALUE
-_BOUNDEDMETRICSBUNDLE.fields_by_name['bounded_metrics'].message_type = _BOUNDEDMETRIC
-DESCRIPTOR.message_types_by_name['BoundedMetric'] = _BOUNDEDMETRIC
-DESCRIPTOR.message_types_by_name['DoubleValue'] = _DOUBLEVALUE
-DESCRIPTOR.message_types_by_name['KeyValue'] = _KEYVALUE
-DESCRIPTOR.message_types_by_name['BoundedMetricsBundle'] = _BOUNDEDMETRICSBUNDLE
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-BoundedMetric = _reflection.GeneratedProtocolMessageType('BoundedMetric', (_message.Message,), {
-  'DESCRIPTOR' : _BOUNDEDMETRIC,
-  '__module__' : 'metrics_pb2'
-  # @@protoc_insertion_point(class_scope:acts.metrics.BoundedMetric)
-  })
-_sym_db.RegisterMessage(BoundedMetric)
-
-DoubleValue = _reflection.GeneratedProtocolMessageType('DoubleValue', (_message.Message,), {
-  'DESCRIPTOR' : _DOUBLEVALUE,
-  '__module__' : 'metrics_pb2'
-  # @@protoc_insertion_point(class_scope:acts.metrics.DoubleValue)
-  })
-_sym_db.RegisterMessage(DoubleValue)
-
-KeyValue = _reflection.GeneratedProtocolMessageType('KeyValue', (_message.Message,), {
-  'DESCRIPTOR' : _KEYVALUE,
-  '__module__' : 'metrics_pb2'
-  # @@protoc_insertion_point(class_scope:acts.metrics.KeyValue)
-  })
-_sym_db.RegisterMessage(KeyValue)
-
-BoundedMetricsBundle = _reflection.GeneratedProtocolMessageType('BoundedMetricsBundle', (_message.Message,), {
-  'DESCRIPTOR' : _BOUNDEDMETRICSBUNDLE,
-  '__module__' : 'metrics_pb2'
-  # @@protoc_insertion_point(class_scope:acts.metrics.BoundedMetricsBundle)
-  })
-_sym_db.RegisterMessage(BoundedMetricsBundle)
-
-
-DESCRIPTOR._options = None
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\030com.android.acts.metrics'
+  _BOUNDEDMETRIC._serialized_start=32
+  _BOUNDEDMETRIC._serialized_end=268
+  _DOUBLEVALUE._serialized_start=270
+  _DOUBLEVALUE._serialized_end=298
+  _KEYVALUE._serialized_start=300
+  _KEYVALUE._serialized_end=338
+  _BOUNDEDMETRICSBUNDLE._serialized_start=340
+  _BOUNDEDMETRICSBUNDLE._serialized_end=416
 # @@protoc_insertion_point(module_scope)
diff --git a/acts/framework/acts/test_runner.py b/acts/framework/acts/test_runner.py
index e4a9da4..7a5eac8 100644
--- a/acts/framework/acts/test_runner.py
+++ b/acts/framework/acts/test_runner.py
@@ -26,7 +26,6 @@
 import json
 import logging
 import os
-import pkgutil
 import sys
 
 from acts import base_test
@@ -108,6 +107,7 @@
             run.
         running: A boolean signifies whether this test run is ongoing or not.
     """
+
     def __init__(self, test_configs, run_list):
         self.test_run_config = test_configs
         self.testbed_name = self.test_run_config.testbed_name
@@ -151,6 +151,7 @@
             A dictionary where keys are test class name strings, values are
             actual test classes that can be instantiated.
         """
+
         def is_testfile_name(name, ext):
             if ext == '.py':
                 if name.endswith('Test') or name.endswith('_test'):
diff --git a/acts/framework/acts/utils.py b/acts/framework/acts/utils.py
index 70becb9..e5e61ef 100755
--- a/acts/framework/acts/utils.py
+++ b/acts/framework/acts/utils.py
@@ -36,7 +36,6 @@
 import traceback
 import zipfile
 from concurrent.futures import ThreadPoolExecutor
-from zeroconf import IPVersion, Zeroconf
 
 from acts import signals
 from acts.controllers.adb_lib.error import AdbError
@@ -49,6 +48,10 @@
 # All Fuchsia devices use this suffix for link-local mDNS host names.
 FUCHSIA_MDNS_TYPE = '_fuchsia._udp.local.'
 
+# Default max seconds it takes to Duplicate Address Detection to finish before
+# assigning an IPv6 address.
+DAD_TIMEOUT_SEC = 30
+
 
 class ActsUtilsError(Exception):
     """Generic error raised for exceptions in ACTS utils."""
@@ -536,7 +539,6 @@
 class TimeoutError(Exception):
     """Exception for timeout decorator related errors.
     """
-    pass
 
 
 def _timeout_handler(signum, frame):
@@ -563,6 +565,7 @@
     """
 
     def decorator(func):
+
         @functools.wraps(func)
         def wrapper(*args, **kwargs):
             if sec:
@@ -1411,44 +1414,38 @@
 
     Returns:
         A list of dictionaries of the the various IP addresses:
-            ipv4_private_local_addresses: Any 192.168, 172.16, 10, or 169.254
-                addresses
-            ipv4_public_addresses: Any IPv4 public addresses
-            ipv6_link_local_addresses: Any fe80:: addresses
-            ipv6_private_local_addresses: Any fd00:: addresses
-            ipv6_public_addresses: Any publicly routable addresses
+            ipv4_private: Any 192.168, 172.16, 10, or 169.254 addresses
+            ipv4_public: Any IPv4 public addresses
+            ipv6_link_local: Any fe80:: addresses
+            ipv6_private_local: Any fd00:: addresses
+            ipv6_public: Any publicly routable addresses
     """
     # Local imports are used here to prevent cyclic dependency.
     from acts.controllers.android_device import AndroidDevice
     from acts.controllers.fuchsia_device import FuchsiaDevice
     from acts.controllers.utils_lib.ssh.connection import SshConnection
-    ipv4_private_local_addresses = []
-    ipv4_public_addresses = []
-    ipv6_link_local_addresses = []
-    ipv6_private_local_addresses = []
-    ipv6_public_addresses = []
+
     is_local = comm_channel == job
     if type(comm_channel) is AndroidDevice:
-        all_interfaces_and_addresses = comm_channel.adb.shell(
-            'ip -o addr | awk \'!/^[0-9]*: ?lo|link\/ether/ {gsub("/", " "); '
-            'print $2" "$4}\'')
-        ifconfig_output = comm_channel.adb.shell('ifconfig %s' % interface)
+        addrs = comm_channel.adb.shell(
+            f'ip -o addr show {interface} | awk \'{{gsub("/", " "); print $4}}\''
+        ).splitlines()
     elif (type(comm_channel) is SshConnection or is_local):
-        all_interfaces_and_addresses = comm_channel.run(
-            'ip -o addr | awk \'!/^[0-9]*: ?lo|link\/ether/ {gsub("/", " "); '
-            'print $2" "$4}\'').stdout
-        ifconfig_output = comm_channel.run('ifconfig %s' % interface).stdout
+        addrs = comm_channel.run(
+            f'ip -o addr show {interface} | awk \'{{gsub("/", " "); print $4}}\''
+        ).stdout.splitlines()
     elif type(comm_channel) is FuchsiaDevice:
-        all_interfaces_and_addresses = []
-        interfaces = comm_channel.netstack_lib.netstackListInterfaces()
-        if interfaces.get('error') is not None:
-            raise ActsUtilsError('Failed with {}'.format(
-                interfaces.get('error')))
+        interfaces = comm_channel.sl4f.netstack_lib.netstackListInterfaces()
+        err = interfaces.get('error')
+        if err is not None:
+            raise ActsUtilsError(f'Failed get_interface_ip_addresses: {err}')
+        addrs = []
         for item in interfaces.get('result'):
+            if item['name'] != interface:
+                continue
             for ipv4_address in item['ipv4_addresses']:
                 ipv4_address = '.'.join(map(str, ipv4_address))
-                all_interfaces_and_addresses.append(
-                    '%s %s' % (item['name'], ipv4_address))
+                addrs.append(ipv4_address)
             for ipv6_address in item['ipv6_addresses']:
                 converted_ipv6_address = []
                 for octet in ipv6_address:
@@ -1457,22 +1454,21 @@
                 ipv6_address = (':'.join(
                     ipv6_address[i:i + 4]
                     for i in range(0, len(ipv6_address), 4)))
-                all_interfaces_and_addresses.append(
-                    '%s %s' %
-                    (item['name'], str(ipaddress.ip_address(ipv6_address))))
-        all_interfaces_and_addresses = '\n'.join(all_interfaces_and_addresses)
-        ifconfig_output = all_interfaces_and_addresses
+                addrs.append(str(ipaddress.ip_address(ipv6_address)))
     else:
         raise ValueError('Unsupported method to send command to device.')
 
-    for interface_line in all_interfaces_and_addresses.split('\n'):
-        if interface != interface_line.split()[0]:
-            continue
-        on_device_ip = ipaddress.ip_address(interface_line.split()[1])
+    ipv4_private_local_addresses = []
+    ipv4_public_addresses = []
+    ipv6_link_local_addresses = []
+    ipv6_private_local_addresses = []
+    ipv6_public_addresses = []
+
+    for addr in addrs:
+        on_device_ip = ipaddress.ip_address(addr)
         if on_device_ip.version == 4:
             if on_device_ip.is_private:
-                if str(on_device_ip) in ifconfig_output:
-                    ipv4_private_local_addresses.append(str(on_device_ip))
+                ipv4_private_local_addresses.append(str(on_device_ip))
             elif on_device_ip.is_global or (
                     # Carrier private doesn't have a property, so we check if
                     # all other values are left unset.
@@ -1481,18 +1477,15 @@
                     and not on_device_ip.is_link_local
                     and not on_device_ip.is_loopback
                     and not on_device_ip.is_multicast):
-                if str(on_device_ip) in ifconfig_output:
-                    ipv4_public_addresses.append(str(on_device_ip))
+                ipv4_public_addresses.append(str(on_device_ip))
         elif on_device_ip.version == 6:
             if on_device_ip.is_link_local:
-                if str(on_device_ip) in ifconfig_output:
-                    ipv6_link_local_addresses.append(str(on_device_ip))
+                ipv6_link_local_addresses.append(str(on_device_ip))
             elif on_device_ip.is_private:
-                if str(on_device_ip) in ifconfig_output:
-                    ipv6_private_local_addresses.append(str(on_device_ip))
+                ipv6_private_local_addresses.append(str(on_device_ip))
             elif on_device_ip.is_global:
-                if str(on_device_ip) in ifconfig_output:
-                    ipv6_public_addresses.append(str(on_device_ip))
+                ipv6_public_addresses.append(str(on_device_ip))
+
     return {
         'ipv4_private': ipv4_private_local_addresses,
         'ipv4_public': ipv4_public_addresses,
@@ -1502,6 +1495,62 @@
     }
 
 
+class AddressTimeout(signals.TestError):
+    pass
+
+
+class MultipleAddresses(signals.TestError):
+    pass
+
+
+def get_addr(comm_channel,
+             interface,
+             addr_type='ipv4_private',
+             timeout_sec=None):
+    """Get the requested type of IP address for an interface; if an address is
+    not available, retry until the timeout has been reached.
+
+    Args:
+        addr_type: Type of address to get as defined by the return value of
+            utils.get_interface_ip_addresses.
+        timeout_sec: Seconds to wait to acquire an address if there isn't one
+            already available. If fetching an IPv4 address, the default is 3
+            seconds. If IPv6, the default is 30 seconds for Duplicate Address
+            Detection.
+
+    Returns:
+        A string containing the requested address.
+
+    Raises:
+        TestAbortClass: timeout_sec is None and invalid addr_type
+        AddressTimeout: No address is available after timeout_sec
+        MultipleAddresses: Several addresses are available
+    """
+    if not timeout_sec:
+        if 'ipv4' in addr_type:
+            timeout_sec = 3
+        elif 'ipv6' in addr_type:
+            timeout_sec = DAD_TIMEOUT_SEC
+        else:
+            raise signals.TestAbortClass(f'Unknown addr_type "{addr_type}"')
+
+    start = time.time()
+    elapsed = 0
+
+    while elapsed <= timeout_sec:
+        ip_addrs = get_interface_ip_addresses(comm_channel,
+                                              interface)[addr_type]
+        if len(ip_addrs) > 1:
+            raise MultipleAddresses(
+                f'Expected only one "{addr_type}" address, got {ip_addrs}')
+        elif len(ip_addrs) == 1:
+            return ip_addrs[0]
+        elapsed = time.time() - start
+
+    raise AddressTimeout(
+        f'No available "{addr_type}" address after {timeout_sec}s')
+
+
 def get_interface_based_on_ip(comm_channel, desired_ip_address):
     """Gets the interface for a particular IP
 
@@ -1525,8 +1574,8 @@
 
 
 def renew_linux_ip_address(comm_channel, interface):
-    comm_channel.run('sudo ifconfig %s down' % interface)
-    comm_channel.run('sudo ifconfig %s up' % interface)
+    comm_channel.run('sudo ip link set %s down' % interface)
+    comm_channel.run('sudo ip link set %s up' % interface)
     comm_channel.run('sudo dhclient -r %s' % interface)
     comm_channel.run('sudo dhclient %s' % interface)
 
@@ -1771,50 +1820,64 @@
     Returns:
         string, IPv6 link-local address
     """
+    from zeroconf import IPVersion, Zeroconf
+
     if not device_mdns_name:
         return None
 
-    interfaces = psutil.net_if_addrs()
-    for interface in interfaces:
-        for addr in interfaces[interface]:
-            address = addr.address.split('%')[0]
-            if addr.family == socket.AF_INET6 and ipaddress.ip_address(
-                    address).is_link_local and address != 'fe80::1':
-                logging.info('Sending mDNS query for device "%s" using "%s"' %
-                             (device_mdns_name, addr.address))
-                try:
-                    zeroconf = Zeroconf(ip_version=IPVersion.V6Only,
-                                        interfaces=[address])
-                except RuntimeError as e:
-                    if 'No adapter found for IP address' in e.args[0]:
-                        # Most likely, a device went offline and its control
-                        # interface was deleted. This is acceptable since the
-                        # device that went offline isn't guaranteed to be the
-                        # device we're searching for.
-                        logging.warning('No adapter found for "%s"' % address)
-                        continue
-                    raise
+    def mdns_query(interface, address):
+        logging.info(
+            f'Sending mDNS query for device "{device_mdns_name}" using "{address}"'
+        )
+        try:
+            zeroconf = Zeroconf(ip_version=IPVersion.V6Only,
+                                interfaces=[address])
+        except RuntimeError as e:
+            if 'No adapter found for IP address' in e.args[0]:
+                # Most likely, a device went offline and its control
+                # interface was deleted. This is acceptable since the
+                # device that went offline isn't guaranteed to be the
+                # device we're searching for.
+                logging.warning('No adapter found for "%s"' % address)
+                return None
+            raise
 
-                device_records = zeroconf.get_service_info(
-                    FUCHSIA_MDNS_TYPE,
-                    device_mdns_name + '.' + FUCHSIA_MDNS_TYPE)
+        device_records = zeroconf.get_service_info(
+            FUCHSIA_MDNS_TYPE, device_mdns_name + '.' + FUCHSIA_MDNS_TYPE)
 
-                if device_records:
-                    for device_address in device_records.parsed_addresses():
-                        device_ip_address = ipaddress.ip_address(
-                            device_address)
-                        scoped_address = '%s%%%s' % (device_address, interface)
-                        if (device_ip_address.version == 6
-                                and device_ip_address.is_link_local
-                                and can_ping(job, dest_ip=scoped_address)):
-                            logging.info('Found device "%s" at "%s"' %
-                                         (device_mdns_name, scoped_address))
-                            zeroconf.close()
-                            del zeroconf
-                            return scoped_address
+        if device_records:
+            for device_address in device_records.parsed_addresses():
+                device_ip_address = ipaddress.ip_address(device_address)
+                scoped_address = '%s%%%s' % (device_address, interface)
+                if (device_ip_address.version == 6
+                        and device_ip_address.is_link_local
+                        and can_ping(job, dest_ip=scoped_address)):
+                    logging.info('Found device "%s" at "%s"' %
+                                 (device_mdns_name, scoped_address))
+                    zeroconf.close()
+                    del zeroconf
+                    return scoped_address
 
-                zeroconf.close()
-                del zeroconf
+        zeroconf.close()
+        del zeroconf
+        return None
+
+    with ThreadPoolExecutor() as executor:
+        futures = []
+
+        interfaces = psutil.net_if_addrs()
+        for interface in interfaces:
+            for addr in interfaces[interface]:
+                address = addr.address.split('%')[0]
+                if addr.family == socket.AF_INET6 and ipaddress.ip_address(
+                        address).is_link_local and address != 'fe80::1':
+                    futures.append(
+                        executor.submit(mdns_query, interface, address))
+
+        for future in futures:
+            addr = future.result()
+            if addr:
+                return addr
 
     logging.error('Unable to find IP address for device "%s"' %
                   device_mdns_name)
@@ -1862,4 +1925,4 @@
             'More than one device matching "device_type" == "{}"'.format(
                 device_type))
 
-    return matches[0]
\ No newline at end of file
+    return matches[0]
diff --git a/acts/framework/setup.py b/acts/framework/setup.py
index 26d59f6..30b1933 100755
--- a/acts/framework/setup.py
+++ b/acts/framework/setup.py
@@ -23,35 +23,33 @@
 import sys
 
 install_requires = [
-    'backoff',
-    'dlipower',
+    # Require an older version of setuptools that does not enforce PEP 440.
+    # This must be added first.
+    'setuptools<66.0.0',
     # Future needs to have a newer version that contains urllib.
     'future>=0.16.0',
-    'grpcio',
     'mobly==1.12.0',
     # Latest version of mock (4.0.0b) causes a number of compatibility issues with ACTS unit tests
     # b/148695846, b/148814743
     'mock==3.0.5',
     'Monsoon',
-    # paramiko-ng is needed vs paramiko as currently paramiko does not support
-    # ed25519 ssh keys, which is what Fuchsia uses.
-    'paramiko-ng',
-    'protobuf>=3.14.0',
+    'paramiko[ed25519]',
     'pylibftdi',
-    'pynacl==1.4.0',
     'pyserial',
     'pyyaml>=5.1',
     'requests',
     'retry',
     'scapy',
     'usbinfo',
-    'xlsxwriter',
     'zeroconf'
 ]
 
 versioned_deps = {
+    'backoff': 'backoff',
     'numpy': 'numpy',
-    'scipy': 'scipy'
+    'scipy': 'scipy',
+    'protobuf': 'protobuf==4.21.5',
+    'grpcio': 'grpcio',
 }
 
 # numpy and scipy version matrix per:
@@ -60,12 +58,18 @@
     versioned_deps['numpy'] = 'numpy<1.22'
     versioned_deps['scipy'] = 'scipy<1.8'
 if sys.version_info < (3, 7):
+    versioned_deps['backoff'] = 'backoff<2.0'
     versioned_deps['numpy'] = 'numpy<1.20'
     versioned_deps['scipy'] = 'scipy<1.6'
+    versioned_deps['protobuf'] = 'protobuf==3.20.1'
+    versioned_deps['grpcio'] = 'grpcio==1.48.2'
     versioned_deps['typing_extensions'] = 'typing_extensions==4.1.1'
+if (sys.version_info.major, sys.version_info.minor) == (3, 6):
+    versioned_deps['dataclasses'] = 'dataclasses==0.8'
 if sys.version_info < (3, 6):
     versioned_deps['numpy'] = 'numpy<1.19'
     versioned_deps['scipy'] = 'scipy<1.5'
+    versioned_deps['typing_extensions'] = 'typing_extensions<4.0.0'
 
 install_requires += list(versioned_deps.values())
 
@@ -103,36 +107,6 @@
         sys.exit(result.returncode)
 
 
-class ActsInstallDependencies(cmd.Command):
-    """Installs only required packages
-
-    Installs all required packages for acts to work. Rather than using the
-    normal install system which creates links with the python egg, pip is
-    used to install the packages.
-    """
-
-    description = 'Install dependencies needed for acts to run on this machine.'
-    user_options = []
-
-    def initialize_options(self):
-        pass
-
-    def finalize_options(self):
-        pass
-
-    def run(self):
-        install_args = [sys.executable, '-m', 'pip', 'install']
-        subprocess.check_call(install_args + ['--upgrade', 'pip'])
-        required_packages = self.distribution.install_requires
-
-        for package in required_packages:
-            self.announce('Installing %s...' % package, log.INFO)
-            subprocess.check_call(install_args +
-                                  ['-v', '--no-cache-dir', package])
-
-        self.announce('Dependencies installed.')
-
-
 class ActsUninstall(cmd.Command):
     """Acts uninstaller.
 
@@ -205,11 +179,13 @@
                      include_package_data=True,
                      tests_require=['pytest'],
                      install_requires=install_requires,
-                     extras_require={'dev': DEV_PACKAGES},
+                     extras_require={
+                         'dev': DEV_PACKAGES,
+                         'digital_loggers_pdu': ['dlipower'],
+                     },
                      scripts=scripts,
                      cmdclass={
                          'test': PyTest,
-                         'install_deps': ActsInstallDependencies,
                          'uninstall': ActsUninstall
                      },
                      url="http://www.android.com/")
diff --git a/acts/framework/tests/acts_relay_controller_test.py b/acts/framework/tests/acts_relay_controller_test.py
index 0aa77b7..d0f3e18 100755
--- a/acts/framework/tests/acts_relay_controller_test.py
+++ b/acts/framework/tests/acts_relay_controller_test.py
@@ -78,50 +78,50 @@
     def test_turn_on_from_off(self):
         self.board.set(self.relay.position, RelayState.NO)
         self.relay.set_nc()
-        self.assertEqual(
-            self.board.get_relay_status(self.relay.position), RelayState.NC)
+        self.assertEqual(self.board.get_relay_status(self.relay.position),
+                         RelayState.NC)
 
     def test_turn_on_from_on(self):
         self.board.set(self.relay.position, RelayState.NC)
         self.relay.set_nc()
-        self.assertEqual(
-            self.board.get_relay_status(self.relay.position), RelayState.NC)
+        self.assertEqual(self.board.get_relay_status(self.relay.position),
+                         RelayState.NC)
 
     def test_turn_off_from_on(self):
         self.board.set(self.relay.position, RelayState.NC)
         self.relay.set_no()
-        self.assertEqual(
-            self.board.get_relay_status(self.relay.position), RelayState.NO)
+        self.assertEqual(self.board.get_relay_status(self.relay.position),
+                         RelayState.NO)
 
     def test_turn_off_from_off(self):
         self.board.set(self.relay.position, RelayState.NO)
         self.relay.set_no()
-        self.assertEqual(
-            self.board.get_relay_status(self.relay.position), RelayState.NO)
+        self.assertEqual(self.board.get_relay_status(self.relay.position),
+                         RelayState.NO)
 
     def test_toggle_off_to_on(self):
         self.board.set(self.relay.position, RelayState.NO)
         self.relay.toggle()
-        self.assertEqual(
-            self.board.get_relay_status(self.relay.position), RelayState.NC)
+        self.assertEqual(self.board.get_relay_status(self.relay.position),
+                         RelayState.NC)
 
     def test_toggle_on_to_off(self):
         self.board.set(self.relay.position, RelayState.NC)
         self.relay.toggle()
-        self.assertEqual(
-            self.board.get_relay_status(self.relay.position), RelayState.NO)
+        self.assertEqual(self.board.get_relay_status(self.relay.position),
+                         RelayState.NO)
 
     def test_set_on(self):
         self.board.set(self.relay.position, RelayState.NO)
         self.relay.set(RelayState.NC)
-        self.assertEqual(
-            self.board.get_relay_status(self.relay.position), RelayState.NC)
+        self.assertEqual(self.board.get_relay_status(self.relay.position),
+                         RelayState.NC)
 
     def test_set_off(self):
         self.board.set(self.relay.position, RelayState.NC)
         self.relay.set(RelayState.NO)
-        self.assertEqual(
-            self.board.get_relay_status(self.relay.position), RelayState.NO)
+        self.assertEqual(self.board.get_relay_status(self.relay.position),
+                         RelayState.NO)
 
     def test_set_foo(self):
         with self.assertRaises(ValueError):
@@ -134,8 +134,8 @@
 
         self.relay.set_nc_for(0)
 
-        self.assertEqual(
-            self.board.get_relay_status(self.relay.position), RelayState.NO)
+        self.assertEqual(self.board.get_relay_status(self.relay.position),
+                         RelayState.NO)
         self.assertEqual(self.board.relay_previous_states[self.relay.position],
                          RelayState.NC)
 
@@ -146,8 +146,8 @@
 
         self.relay.set_no_for(0)
 
-        self.assertEqual(
-            self.board.get_relay_status(self.relay.position), RelayState.NC)
+        self.assertEqual(self.board.get_relay_status(self.relay.position),
+                         RelayState.NC)
         self.assertEqual(self.board.relay_previous_states[self.relay.position],
                          RelayState.NO)
 
@@ -165,8 +165,8 @@
         self.board.set(new_relay.position, RelayState.NO)
         new_relay.clean_up()
 
-        self.assertEqual(
-            self.board.get_relay_status(new_relay.position), RelayState.NO)
+        self.assertEqual(self.board.get_relay_status(new_relay.position),
+                         RelayState.NO)
 
     def test_clean_up_default_off(self):
         new_relay = Relay(self.board, 0)
@@ -174,8 +174,8 @@
         self.board.set(new_relay.position, RelayState.NC)
         new_relay.clean_up()
 
-        self.assertEqual(
-            self.board.get_relay_status(new_relay.position), RelayState.NO)
+        self.assertEqual(self.board.get_relay_status(new_relay.position),
+                         RelayState.NO)
 
     def test_clean_up_original_state_none(self):
         val = 'STAYS_THE_SAME'
@@ -208,8 +208,10 @@
             file.write(self.RELAY_ON_PAGE_CONTENTS)
 
         self.config = ({
-            'name': 'SSBoard',
-            'base_url': self.test_dir,
+            'name':
+            'SSBoard',
+            'base_url':
+            self.test_dir,
             'relays': [{
                 'name': '0',
                 'relay_pos': 0
@@ -293,18 +295,18 @@
     def test_get_relay_status_status_dict_none(self):
         self._set_status_page('1111111111111111')
         self.ss_board.status_dict = None
-        self.assertEqual(
-            self.ss_board.get_relay_status(self.r0.position), RelayState.NC)
+        self.assertEqual(self.ss_board.get_relay_status(self.r0.position),
+                         RelayState.NC)
 
     def test_get_relay_status_status_dict_on(self):
         self.r0.set(RelayState.NC)
-        self.assertEqual(
-            self.ss_board.get_relay_status(self.r0.position), RelayState.NC)
+        self.assertEqual(self.ss_board.get_relay_status(self.r0.position),
+                         RelayState.NC)
 
     def test_get_relay_status_status_dict_off(self):
         self.r0.set(RelayState.NO)
-        self.assertEqual(
-            self.ss_board.get_relay_status(self.r0.position), RelayState.NO)
+        self.assertEqual(self.ss_board.get_relay_status(self.r0.position),
+                         RelayState.NO)
 
     def test_set_on(self):
         patch_path = 'acts.controllers.relay_lib.sain_smart_board.urlopen'
@@ -312,7 +314,8 @@
             board = SainSmartBoard(self.config)
             board.status_dict = {}
             board.set(self.r0.position, RelayState.NC)
-        urlopen.assert_called_once_with('%s%s' % (self.ss_board.base_url, '01'))
+        urlopen.assert_called_once_with('%s%s' %
+                                        (self.ss_board.base_url, '01'))
 
     def test_set_off(self):
         patch_path = 'acts.controllers.relay_lib.sain_smart_board.urlopen'
@@ -320,7 +323,8 @@
             board = SainSmartBoard(self.config)
             board.status_dict = {}
             board.set(self.r0.position, RelayState.NO)
-        urlopen.assert_called_once_with('%s%s' % (self.ss_board.base_url, '00'))
+        urlopen.assert_called_once_with('%s%s' %
+                                        (self.ss_board.base_url, '00'))
 
     def test_connection_error_no_tux(self):
         default_status_msg = self.STATUS_MSG
@@ -672,8 +676,10 @@
         rig.relays['r0'] = self.r0
         rig.relays['r1'] = self.r1
         config = {
-            'type': 'SomeInvalidType',
-            'name': '.',
+            'type':
+            'SomeInvalidType',
+            'name':
+            '.',
             'relays': [{
                 'name': 'r0',
                 'pos': 'MockBoard/0'
@@ -702,12 +708,11 @@
 class TestFuguRemote(unittest.TestCase):
     def setUp(self):
         Relay.transition_wait_time = 0
-        self.mock_rig = RelayRigMock({
-            "boards": [{
+        self.mock_rig = RelayRigMock(
+            {"boards": [{
                 'name': 'MockBoard',
                 'type': 'FuguMockBoard'
-            }]
-        })
+            }]})
         self.mock_board = self.mock_rig.boards['MockBoard']
         self.fugu_config = {
             'type': 'FuguRemote',
@@ -750,7 +755,6 @@
         fugu = fugu_remote.FuguRemote(self.fugu_config, self.mock_rig)
         fugu.setup()
         self.assertEqual(self.mock_board.get_relay_status(0), RelayState.NC)
-        pass
 
     def press_button_success(self, relay_position):
         self.assertEqual(self.mock_board.relay_states[relay_position],
diff --git a/acts/framework/tests/acts_test_decorators_test.py b/acts/framework/tests/acts_test_decorators_test.py
index 58455d3..c24bb12 100755
--- a/acts/framework/tests/acts_test_decorators_test.py
+++ b/acts/framework/tests/acts_test_decorators_test.py
@@ -157,6 +157,7 @@
 
     def test_function_name(self):
         """Test test_func.__name__ returns its original unwrapped name"""
+
         @test_decorators.test_info(uuid='SOME_UID')
         def test_func():
             pass
@@ -165,15 +166,16 @@
 
     def test_function_doc(self):
         """Test test_func.__doc__ returns its original unwrapped doc string"""
+
         @test_decorators.test_info(uuid='SOME_UID')
         def test_func():
             """DOC_STRING"""
-            pass
 
         self.assertEqual(test_func.__doc__, "DOC_STRING")
 
     def test_function_module(self):
         """Test test_func.__module__ returns its original unwrapped module"""
+
         @test_decorators.test_info(uuid='SOME_UID')
         def test_func():
             pass
@@ -196,7 +198,7 @@
     def __init__(self):
         self.user_params = {
             'testtracker_properties': 'tt_prop_a=param_a,'
-                                      'tt_prop_b=param_b',
+            'tt_prop_b=param_b',
             'param_a': 'prop_a_value',
             'param_b': 'prop_b_value',
         }
@@ -209,7 +211,6 @@
 
 
 class TestTrackerInfoDecoratorFuncTests(unittest.TestCase):
-
     @mock.patch('acts.test_decorators.TestTrackerResultsWriter')
     def test_write_to_testtracker_no_effort_name_no_ad(self, mock_writer):
         """Should not set the TT Effort name."""
@@ -217,9 +218,10 @@
             FakeTest().test_case()
 
         testtracker_properties = mock_writer.call_args[0][1]
-        self.assertEqual(testtracker_properties,
-                         {'tt_prop_a': 'prop_a_value',
-                          'tt_prop_b': 'prop_b_value'})
+        self.assertEqual(testtracker_properties, {
+            'tt_prop_a': 'prop_a_value',
+            'tt_prop_b': 'prop_b_value'
+        })
 
     @mock.patch('acts.test_decorators.TestTrackerResultsWriter')
     def test_write_to_testtracker_no_effort_name_has_ad(self, mock_writer):
@@ -231,10 +233,12 @@
             fake_test.test_case()
 
         testtracker_properties = mock_writer.call_args[0][1]
-        self.assertEqual(testtracker_properties,
-                         {'tt_prop_a': 'prop_a_value',
-                          'tt_prop_b': 'prop_b_value',
-                          KEY_EFFORT_NAME: 'EFFORT_NAME'})
+        self.assertEqual(
+            testtracker_properties, {
+                'tt_prop_a': 'prop_a_value',
+                'tt_prop_b': 'prop_b_value',
+                KEY_EFFORT_NAME: 'EFFORT_NAME'
+            })
 
     @mock.patch('acts.test_decorators.TestTrackerResultsWriter')
     def test_write_to_testtracker_has_effort_name_has_ad(self, mock_writer):
@@ -243,17 +247,19 @@
         fake_test.android_devices = [mock.Mock()]
         fake_test.android_devices[0].build_info = {'build_id': 'BAD_EFFORT'}
         fake_test.user_params['testtracker_properties'] += (
-                    ',' + KEY_EFFORT_NAME + '=param_effort_name')
+            ',' + KEY_EFFORT_NAME + '=param_effort_name')
         fake_test.user_params['param_effort_name'] = 'GOOD_EFFORT_NAME'
 
         with self.assertRaises(signals.TestPass):
             fake_test.test_case()
 
         testtracker_properties = mock_writer.call_args[0][1]
-        self.assertEqual(testtracker_properties,
-                         {'tt_prop_a': 'prop_a_value',
-                          'tt_prop_b': 'prop_b_value',
-                          KEY_EFFORT_NAME: 'GOOD_EFFORT_NAME'})
+        self.assertEqual(
+            testtracker_properties, {
+                'tt_prop_a': 'prop_a_value',
+                'tt_prop_b': 'prop_b_value',
+                KEY_EFFORT_NAME: 'GOOD_EFFORT_NAME'
+            })
 
     @mock.patch('acts.test_decorators.TestTrackerResultsWriter')
     def test_no_testtracker_properties_provided(self, mock_writer):
@@ -289,7 +295,6 @@
         self.assertFalse(mock_writer.called)
 
 
-
 class TestDecoratorIntegrationTests(unittest.TestCase):
     @classmethod
     def setUpClass(cls):
@@ -390,6 +395,7 @@
 
     def test_teardown_and_setup_are_called_between_test_cases(self):
         mock_test_class = mock.Mock()
+
         @test_decorators.repeated_test(1, 1)
         def test_case(*_):
             raise signals.TestFailure('Failed')
diff --git a/acts/framework/tests/acts_utils_test.py b/acts/framework/tests/acts_utils_test.py
index 601dc45..c79b7a1 100755
--- a/acts/framework/tests/acts_utils_test.py
+++ b/acts/framework/tests/acts_utils_test.py
@@ -15,6 +15,7 @@
 #   limitations under the License.
 
 import logging
+import subprocess
 import time
 import unittest
 
@@ -25,49 +26,19 @@
 from acts.controllers.adb_lib.error import AdbError
 from acts.controllers.android_device import AndroidDevice
 from acts.controllers.fuchsia_device import FuchsiaDevice
+from acts.controllers.fuchsia_lib.sl4f import SL4F
+from acts.controllers.fuchsia_lib.ssh import SSHConfig, SSHProvider, SSHResult
 from acts.controllers.utils_lib.ssh.connection import SshConnection
 from acts.libs.proc import job
 
 PROVISIONED_STATE_GOOD = 1
 
-MOCK_IP_ADDRESSES = """eno1 100.127.110.79
-eno1 2401:fa00:480:7a00:8d4f:85ff:cc5c:787e
-eno1 2401:fa00:480:7a00:459:b993:fcbf:1419
-eno1 fe80::c66d:3c75:2cec:1d72
-enx00e04c000d06 192.168.42.220
-enx00e04c000d06 fe80::2c68:f1b7:eaaa:52e7"""
+MOCK_ENO1_IP_ADDRESSES = """100.127.110.79
+2401:fa00:480:7a00:8d4f:85ff:cc5c:787e
+2401:fa00:480:7a00:459:b993:fcbf:1419
+fe80::c66d:3c75:2cec:1d72"""
 
-MOCK_IFCONFIG_OUTPUT = """eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
-        inet 100.127.110.79  netmask 255.255.255.0  broadcast 100.127.110.255
-        inet6 2401:fa00:480:7a00:8d4f:85ff:cc5c:787e  prefixlen 64  scopeid 0x0<global>
-        inet6 fe80::c66d:3c75:2cec:1d72  prefixlen 64  scopeid 0x20<link>
-        inet6 2401:fa00:480:7a00:459:b993:fcbf:1419  prefixlen 64  scopeid 0x0<global>
-        ether 54:b2:03:13:36:05  txqueuelen 1000  (Ethernet)
-        RX packets 32943262  bytes 13324306863 (13.3 GB)
-        RX errors 669  dropped 0  overruns 0  frame 669
-        TX packets 4778580  bytes 3012041798 (3.0 GB)
-        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
-        device interrupt 16  memory 0xdf200000-df220000
-
-enx00e04c000d06: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
-        inet 192.168.42.220  netmask 255.255.255.0  broadcast 192.168.42.255
-        inet6 fe80::2c68:f1b7:eaaa:52e7  prefixlen 64  scopeid 0x20<link>
-        ether 00:e0:4c:00:0d:06  txqueuelen 1000  (Ethernet)
-        RX packets 10212416  bytes 3204008175 (3.2 GB)
-        RX errors 0  dropped 0  overruns 0  frame 0
-        TX packets 9868425  bytes 5641667955 (5.6 GB)
-        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
-
-lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
-        inet 127.0.0.1  netmask 255.0.0.0
-        inet6 ::1  prefixlen 128  scopeid 0x10<host>
-        loop  txqueuelen 1000  (Local Loopback)
-        RX packets 42779835  bytes 6144028882 (6.1 GB)
-        RX errors 0  dropped 0  overruns 0  frame 0
-        TX packets 42779835  bytes 6144028882 (6.1 GB)
-        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
-
-"""
+MOCK_WLAN1_IP_ADDRESSES = ""
 
 FUCHSIA_INTERFACES = {
     'id':
@@ -155,19 +126,6 @@
     'ipv6_public': []
 }
 
-FUCHSIA_INIT_SERVER = ('acts.controllers.fuchsia_device.FuchsiaDevice.'
-                       'init_sl4f_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.'
-                          'start_services')
-FUCHSIA_NETSTACK_LIST_INTERFACES = (
-    'acts.controllers.'
-    'fuchsia_lib.netstack.netstack_lib.'
-    'FuchsiaNetstackLib.netstackListInterfaces')
-
 
 class ByPassSetupWizardTests(unittest.TestCase):
     """This test class for unit testing acts.utils.bypass_setup_wizard."""
@@ -459,10 +417,8 @@
     @mock.patch('acts.libs.proc.job.run')
     def test_local_get_interface_ip_addresses_full(self, job_mock):
         job_mock.side_effect = [
-            job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'),
+            job.Result(stdout=bytes(MOCK_ENO1_IP_ADDRESSES, 'utf-8'),
                        encoding='utf-8'),
-            job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'),
-                       encoding='utf-8')
         ]
         self.assertEqual(utils.get_interface_ip_addresses(job, 'eno1'),
                          CORRECT_FULL_IP_LIST)
@@ -470,10 +426,8 @@
     @mock.patch('acts.libs.proc.job.run')
     def test_local_get_interface_ip_addresses_empty(self, job_mock):
         job_mock.side_effect = [
-            job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'),
+            job.Result(stdout=bytes(MOCK_WLAN1_IP_ADDRESSES, 'utf-8'),
                        encoding='utf-8'),
-            job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'),
-                       encoding='utf-8')
         ]
         self.assertEqual(utils.get_interface_ip_addresses(job, 'wlan1'),
                          CORRECT_EMPTY_IP_LIST)
@@ -481,10 +435,8 @@
     @mock.patch('acts.controllers.utils_lib.ssh.connection.SshConnection.run')
     def test_ssh_get_interface_ip_addresses_full(self, ssh_mock):
         ssh_mock.side_effect = [
-            job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'),
+            job.Result(stdout=bytes(MOCK_ENO1_IP_ADDRESSES, 'utf-8'),
                        encoding='utf-8'),
-            job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'),
-                       encoding='utf-8')
         ]
         self.assertEqual(
             utils.get_interface_ip_addresses(SshConnection('mock_settings'),
@@ -493,10 +445,8 @@
     @mock.patch('acts.controllers.utils_lib.ssh.connection.SshConnection.run')
     def test_ssh_get_interface_ip_addresses_empty(self, ssh_mock):
         ssh_mock.side_effect = [
-            job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'),
+            job.Result(stdout=bytes(MOCK_WLAN1_IP_ADDRESSES, 'utf-8'),
                        encoding='utf-8'),
-            job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'),
-                       encoding='utf-8')
         ]
         self.assertEqual(
             utils.get_interface_ip_addresses(SshConnection('mock_settings'),
@@ -507,7 +457,7 @@
     def test_android_get_interface_ip_addresses_full(self, is_bootloader,
                                                      adb_mock):
         adb_mock().shell.side_effect = [
-            MOCK_IP_ADDRESSES, MOCK_IFCONFIG_OUTPUT
+            MOCK_ENO1_IP_ADDRESSES,
         ]
         self.assertEqual(
             utils.get_interface_ip_addresses(AndroidDevice(), 'eno1'),
@@ -518,43 +468,79 @@
     def test_android_get_interface_ip_addresses_empty(self, is_bootloader,
                                                       adb_mock):
         adb_mock().shell.side_effect = [
-            MOCK_IP_ADDRESSES, MOCK_IFCONFIG_OUTPUT
+            MOCK_WLAN1_IP_ADDRESSES,
         ]
         self.assertEqual(
             utils.get_interface_ip_addresses(AndroidDevice(), 'wlan1'),
             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('acts.controllers.fuchsia_device.FuchsiaDevice.sl4f',
+                new_callable=mock.PropertyMock)
+    @mock.patch('acts.controllers.fuchsia_device.FuchsiaDevice.ffx',
+                new_callable=mock.PropertyMock)
+    @mock.patch('acts.controllers.fuchsia_lib.utils_lib.wait_for_port')
+    @mock.patch('acts.controllers.fuchsia_lib.ssh.SSHProvider.run')
+    @mock.patch(
+        'acts.controllers.fuchsia_lib.sl4f.SL4F._verify_sl4f_connection')
+    @mock.patch('acts.controllers.fuchsia_device.'
+                'FuchsiaDevice._set_control_path_config')
+    @mock.patch('acts.controllers.'
+                'fuchsia_lib.netstack.netstack_lib.'
+                'FuchsiaNetstackLib.netstackListInterfaces')
     def test_fuchsia_get_interface_ip_addresses_full(
-            self, list_interfaces_mock, start_services_mock, control_path_mock,
-            ffx_mock, fuchsia_device_mock):
-        # Will never actually be created/used.
+            self, list_interfaces_mock, control_path_mock,
+            verify_sl4f_conn_mock, ssh_run_mock, wait_for_port_mock,
+            ffx_mock, sl4f_mock):
+        # Configure the log path which is required by ACTS logger.
         logging.log_path = '/tmp/unit_test_garbage'
 
+        ssh = SSHProvider(SSHConfig('192.168.1.1', 22, '/dev/null'))
+        ssh_run_mock.return_value = SSHResult(
+            subprocess.CompletedProcess([], 0, stdout=b'', stderr=b''))
+
+        # Don't try to wait for the SL4F server to start; it's not being used.
+        wait_for_port_mock.return_value = None
+
+        sl4f_mock.return_value = SL4F(ssh, 'http://192.168.1.1:80')
+        verify_sl4f_conn_mock.return_value = None
+
         list_interfaces_mock.return_value = FUCHSIA_INTERFACES
-        fuchsia_device_mock.return_value = None
         self.assertEqual(
             utils.get_interface_ip_addresses(
                 FuchsiaDevice({'ip': '192.168.1.1'}), 'eno1'),
             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('acts.controllers.fuchsia_device.FuchsiaDevice.sl4f',
+                new_callable=mock.PropertyMock)
+    @mock.patch('acts.controllers.fuchsia_device.FuchsiaDevice.ffx',
+                new_callable=mock.PropertyMock)
+    @mock.patch('acts.controllers.fuchsia_lib.utils_lib.wait_for_port')
+    @mock.patch('acts.controllers.fuchsia_lib.ssh.SSHProvider.run')
+    @mock.patch(
+        'acts.controllers.fuchsia_lib.sl4f.SL4F._verify_sl4f_connection')
+    @mock.patch('acts.controllers.fuchsia_device.'
+                'FuchsiaDevice._set_control_path_config')
+    @mock.patch('acts.controllers.'
+                'fuchsia_lib.netstack.netstack_lib.'
+                'FuchsiaNetstackLib.netstackListInterfaces')
     def test_fuchsia_get_interface_ip_addresses_empty(
-            self, list_interfaces_mock, start_services_mock, control_path_mock,
-            ffx_mock, fuchsia_device_mock):
-        # Will never actually be created/used.
+            self, list_interfaces_mock, control_path_mock,
+            verify_sl4f_conn_mock, ssh_run_mock, wait_for_port_mock,
+            ffx_mock, sl4f_mock):
+        # Configure the log path which is required by ACTS logger.
         logging.log_path = '/tmp/unit_test_garbage'
 
+        ssh = SSHProvider(SSHConfig('192.168.1.1', 22, '/dev/null'))
+        ssh_run_mock.return_value = SSHResult(
+            subprocess.CompletedProcess([], 0, stdout=b'', stderr=b''))
+
+        # Don't try to wait for the SL4F server to start; it's not being used.
+        wait_for_port_mock.return_value = None
+
+        sl4f_mock.return_value = SL4F(ssh, 'http://192.168.1.1:80')
+        verify_sl4f_conn_mock.return_value = None
+
         list_interfaces_mock.return_value = FUCHSIA_INTERFACES
-        fuchsia_device_mock.return_value = None
         self.assertEqual(
             utils.get_interface_ip_addresses(
                 FuchsiaDevice({'ip': '192.168.1.1'}), 'wlan1'),
diff --git a/acts/framework/tests/controllers/ap_lib/dhcp_config_test.py b/acts/framework/tests/controllers/ap_lib/dhcp_config_test.py
index 754655f..701accc 100644
--- a/acts/framework/tests/controllers/ap_lib/dhcp_config_test.py
+++ b/acts/framework/tests/controllers/ap_lib/dhcp_config_test.py
@@ -16,7 +16,6 @@
 
 import ipaddress
 import unittest
-from unittest.mock import patch
 
 from acts.controllers.ap_lib.dhcp_config import DhcpConfig, Subnet, StaticMapping
 
diff --git a/acts/framework/tests/controllers/ap_lib/radio_measurement_test.py b/acts/framework/tests/controllers/ap_lib/radio_measurement_test.py
new file mode 100644
index 0000000..d72840d
--- /dev/null
+++ b/acts/framework/tests/controllers/ap_lib/radio_measurement_test.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import unittest
+
+from acts.controllers.ap_lib.radio_measurement import BssidInformation, BssidInformationCapabilities, NeighborReportElement, PhyType
+
+EXPECTED_BSSID = '01:23:45:ab:cd:ef'
+EXPECTED_BSSID_INFO_CAP = BssidInformationCapabilities(
+    spectrum_management=True, qos=True, apsd=True, radio_measurement=True)
+EXPECTED_OP_CLASS = 81
+EXPECTED_CHAN = 11
+EXPECTED_PHY = PhyType.HT
+EXPECTED_BSSID_INFO = BssidInformation(capabilities=EXPECTED_BSSID_INFO_CAP,
+                                       high_throughput=True)
+
+
+class RadioMeasurementTest(unittest.TestCase):
+    def test_bssid_information_capabilities(self):
+        self.assertTrue(EXPECTED_BSSID_INFO_CAP.spectrum_management)
+        self.assertTrue(EXPECTED_BSSID_INFO_CAP.qos)
+        self.assertTrue(EXPECTED_BSSID_INFO_CAP.apsd)
+        self.assertTrue(EXPECTED_BSSID_INFO_CAP.radio_measurement)
+        # Must also test the numeric representation.
+        self.assertEqual(int(EXPECTED_BSSID_INFO_CAP), 0b111100)
+
+    def test_bssid_information(self):
+        self.assertEqual(EXPECTED_BSSID_INFO.capabilities,
+                         EXPECTED_BSSID_INFO_CAP)
+        self.assertEqual(EXPECTED_BSSID_INFO.high_throughput, True)
+        # Must also test the numeric representation.
+        self.assertEqual(int(EXPECTED_BSSID_INFO),
+                         0b10001111000100000000000000000000)
+
+    def test_neighbor_report_element(self):
+        element = NeighborReportElement(bssid=EXPECTED_BSSID,
+                                        bssid_information=EXPECTED_BSSID_INFO,
+                                        operating_class=EXPECTED_OP_CLASS,
+                                        channel_number=EXPECTED_CHAN,
+                                        phy_type=EXPECTED_PHY)
+        self.assertEqual(element.bssid, EXPECTED_BSSID)
+        self.assertEqual(element.bssid_information, EXPECTED_BSSID_INFO)
+        self.assertEqual(element.operating_class, EXPECTED_OP_CLASS)
+        self.assertEqual(element.channel_number, EXPECTED_CHAN)
+        self.assertEqual(element.phy_type, EXPECTED_PHY)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/acts/framework/tests/controllers/ap_lib/radvd_test.py b/acts/framework/tests/controllers/ap_lib/radvd_test.py
index 4f23b20..25513c7 100644
--- a/acts/framework/tests/controllers/ap_lib/radvd_test.py
+++ b/acts/framework/tests/controllers/ap_lib/radvd_test.py
@@ -15,7 +15,6 @@
 #   limitations under the License.
 
 import os
-import time
 import unittest
 from unittest.mock import patch
 
@@ -25,7 +24,6 @@
 
 from acts.controllers.ap_lib.radvd_config import RadvdConfig
 
-RADVD_PREFIX = 'fd00::/64'
 SEARCH_FILE = ('acts.controllers.utils_lib.commands.shell.'
                'ShellCommand.search_file')
 DELETE_FILE = ('acts.controllers.utils_lib.commands.shell.ShellCommand.'
@@ -160,11 +158,7 @@
     def test_write_configs_simple(self, write_file, delete_file):
         delete_file.side_effect = delete_file_mock
         write_file.side_effect = write_configs_mock
-        basic_radvd_config = RadvdConfig(
-            prefix=RADVD_PREFIX,
-            adv_send_advert=radvd_constants.ADV_SEND_ADVERT_ON,
-            adv_on_link=radvd_constants.ADV_ON_LINK_ON,
-            adv_autonomous=radvd_constants.ADV_AUTONOMOUS_ON)
+        basic_radvd_config = RadvdConfig()
         radvd_mock = Radvd('mock_runner', 'wlan0')
         radvd_mock._write_configs(basic_radvd_config)
         radvd_config = radvd_mock._config_file
@@ -178,9 +172,8 @@
         delete_file.side_effect = delete_file_mock
         write_file.side_effect = write_configs_mock
         complex_radvd_config = RadvdConfig(
-            prefix=RADVD_PREFIX,
             clients=['fe80::c66d:3c75:2cec:1d72', 'fe80::c66d:3c75:2cec:1d73'],
-            route=RADVD_PREFIX,
+            route=radvd_constants.DEFAULT_PREFIX,
             rdnss=[
                 '2401:fa00:480:7a00:4d56:5373:4549:1e29',
                 '2401:fa00:480:7a00:4d56:5373:4549:1e30',
diff --git a/acts/framework/tests/controllers/ap_lib/wireless_network_management_test.py b/acts/framework/tests/controllers/ap_lib/wireless_network_management_test.py
new file mode 100644
index 0000000..45dfedb
--- /dev/null
+++ b/acts/framework/tests/controllers/ap_lib/wireless_network_management_test.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import unittest
+
+from acts.controllers.ap_lib.radio_measurement import BssidInformation, NeighborReportElement, PhyType
+from acts.controllers.ap_lib.wireless_network_management import BssTransitionCandidateList, BssTransitionManagementRequest
+
+EXPECTED_NEIGHBOR_1 = NeighborReportElement(
+    bssid='01:23:45:ab:cd:ef',
+    bssid_information=BssidInformation(),
+    operating_class=81,
+    channel_number=1,
+    phy_type=PhyType.HT)
+EXPECTED_NEIGHBOR_2 = NeighborReportElement(
+    bssid='cd:ef:ab:45:67:89',
+    bssid_information=BssidInformation(),
+    operating_class=121,
+    channel_number=149,
+    phy_type=PhyType.VHT)
+EXPECTED_NEIGHBORS = [EXPECTED_NEIGHBOR_1, EXPECTED_NEIGHBOR_2]
+EXPECTED_CANDIDATE_LIST = BssTransitionCandidateList(EXPECTED_NEIGHBORS)
+
+
+class WirelessNetworkManagementTest(unittest.TestCase):
+    def test_bss_transition_management_request(self):
+        request = BssTransitionManagementRequest(
+            disassociation_imminent=True,
+            abridged=True,
+            candidate_list=EXPECTED_NEIGHBORS)
+        self.assertTrue(request.disassociation_imminent)
+        self.assertTrue(request.abridged)
+        self.assertIn(EXPECTED_NEIGHBOR_1, request.candidate_list)
+        self.assertIn(EXPECTED_NEIGHBOR_2, request.candidate_list)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/acts/framework/tests/controllers/pdu_lib/synaccess/np02b_test.py b/acts/framework/tests/controllers/pdu_lib/synaccess/np02b_test.py
index c0af8c8..9aa3e53 100644
--- a/acts/framework/tests/controllers/pdu_lib/synaccess/np02b_test.py
+++ b/acts/framework/tests/controllers/pdu_lib/synaccess/np02b_test.py
@@ -16,7 +16,6 @@
 """Python unittest module for pdu_lib.synaccess.np02b"""
 
 import unittest
-from unittest.mock import Mock
 from unittest.mock import patch
 
 from acts.controllers.pdu import PduError
diff --git a/acts/framework/tests/controllers/rohdeschwarz_lib/cmw500_scenario_generator_test.py b/acts/framework/tests/controllers/rohdeschwarz_lib/cmw500_scenario_generator_test.py
new file mode 100644
index 0000000..c3ce108
--- /dev/null
+++ b/acts/framework/tests/controllers/rohdeschwarz_lib/cmw500_scenario_generator_test.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2023 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#           http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""Provides unittests for cmw500_scenario_generator"""
+
+from __future__ import print_function
+
+import unittest
+
+from acts.controllers.rohdeschwarz_lib import cmw500_scenario_generator as cg
+
+
+class CMW500ScenarioTest(unittest.TestCase):
+    """Unit test for CMW500Scenario class."""
+
+    def test_1CC_1x1(self):
+        """Verify name and route text for: 1CC, MIMO: 1x1, BANDS: 3"""
+        self._gen_equals((3, ), (1, ), "SCEL", "SUA1,RF1C,RX1,RF1C,TX1")
+
+    def test_1CC_2x2(self):
+        """Verify name and route text for: 1CC, MIMO: 2x2, BANDS: 3"""
+        self._gen_equals((3, ), (2, ), "TRO",
+                         "SUA1,RF1C,RX1,RF1C,TX1,RF3C,TX2")
+
+    def test_2CC_4x4(self):
+        """Verify name and route text for: 1CC, MIMO: 4x4, BANDS: 3"""
+        self._gen_equals(
+            (3, ),
+            (4, ),
+            "AD",
+            "SUA1,RF1C,RX1,RF1C,TX1,RF3C,TX2,RF2C,TX3,RF4C,TX4",
+        )
+
+    def _gen_equals(self, bands, antennas, scenario_name, scenario_route):
+        scenario = cg.get_scenario(bands, antennas)
+        print("want scenario name: {}".format(scenario_name))
+        print("got scenario name: {}".format(scenario.name))
+        self.assertTrue(scenario.name == scenario_name)
+        print("want scenario route: {}".format(scenario_route))
+        print("got scenario route: {}".format(scenario.routing))
+        self.assertTrue(scenario.routing == scenario_route)
+
+
+class CMW500ScenarioGeneratorTest(unittest.TestCase):
+    """Unit test for CMW500ScenarioGenerator class."""
+
+    def test_2CC_2x2_2x2_B3B3(self):
+        """Verify generated output for: 2CC, MIMO: 2x2 2x2, BANDS: 3 3"""
+        self._gen_equals((3, 3), (2, 2), [[(1, 1), (3, 2)], [(1, 1), (3, 2)]])
+
+    def test_2CC_2x2_2x2_B2B3(self):
+        """Verify generated output for: 2CC, MIMO: 2x2 2x2, BANDS: 2 3"""
+        self._gen_equals((2, 3), (2, 2), [[(1, 1), (3, 2)], [(1, 3), (3, 4)]])
+
+    def test_2CC_4x4_4x4_B3B3(self):
+        """Verify generated output for: 3CC, MIMO: 4x4 4x4, BANDS: 3 3"""
+        self._gen_equals(
+            (3, 3),
+            (4, 4),
+            [
+                [(1, 1), (3, 2), (2, 3), (4, 4)],
+                [(1, 1), (3, 2), (2, 3), (4, 4)],
+            ],
+        )
+
+    def test_3CC_2x2_1x1_1x1_B3B5B7(self):
+        """Verify generated output for: 3CC, MIMO: 2x2 1x1 1x1, BANDS: 3 5 7"""
+        self._gen_equals((3, 5, 7), (2, 1, 1), [[(1, 1),
+                                                 (3, 2)], [(1, 3)], [(3, 4)]])
+
+    def test_3CC_2x2_2x2_2x2_B3B5B3(self):
+        """Verify generated output for: 3CC, MIMO: 2x2 2x2 2x2, BANDS: 3 5 3"""
+        self._gen_equals(
+            (3, 5, 3),
+            (2, 2, 2),
+            [[(1, 1), (3, 2)], [(1, 3), (3, 4)], [(1, 1), (3, 2)]],
+        )
+
+    def test_3CC_2x2_2x2_2x2_B3B5B7(self):
+        """Verify generated output for: 3CC, MIMO: 2x2 2x2 2x2, BANDS: 3 5 7"""
+        self._raises((3, 5, 7), (2, 2, 2))
+
+    def test_4CC_1x1_1x1_1x1_1x1_B2B3B5B7(self):
+        """Verify generated output for: 4CC, MIMO: 2x2 2x2 2x2, BANDS: 2 3 5 7"""
+        self._gen_equals((2, 3, 5, 7), (1, 1, 1, 1),
+                         [[(1, 1)], [(1, 3)], [(3, 2)], [(3, 4)]])
+
+    def test_4CC_2x2_2x2_2x2_2x2_B2B3B2B3(self):
+        """Verify generated output for: 4CC, MIMO: 2x2 2x2 2x2 2x2, BANDS: 2 3 2 3"""
+        self._gen_equals(
+            (2, 3, 2, 3),
+            (2, 2, 2, 2),
+            [
+                [(1, 1), (3, 2)],
+                [(1, 3), (3, 4)],
+                [(1, 1), (3, 2)],
+                [(1, 3), (3, 4)],
+            ],
+        )
+
+    def _gen_equals(self, bands, antennas, want):
+        # ensure expected port configurations match
+        got = self._gen(bands, antennas)
+        print(f"want config: {want}")
+        print(f"got config: {got}")
+        self.assertTrue(got == want)
+        # ensure antenna matches with original
+        scenario = cg.get_scenario(bands, antennas)
+        antennas_got = cg.get_antennas(scenario.name)
+        print(f"want antennas: {antennas}")
+        print(f"got antennas: {antennas_got}")
+        self.assertTrue(tuple(antennas_got) == tuple(antennas))
+
+    def _raises(self, bands, antennas):
+        with self.assertRaises(ValueError):
+            self._gen(bands, antennas)
+
+    def _gen(self, bands, antennas):
+        gen = cg.CMW500ScenarioGenerator()
+        return [gen.get_next(b, a) for b, a in zip(bands, antennas)]
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/acts/framework/tests/event/event_bus_test.py b/acts/framework/tests/event/event_bus_test.py
index 6c2cfb3..d55a75a 100755
--- a/acts/framework/tests/event/event_bus_test.py
+++ b/acts/framework/tests/event/event_bus_test.py
@@ -13,7 +13,6 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-import threading
 import unittest
 from unittest import TestCase
 
@@ -88,7 +87,8 @@
         mock_type = Mock()
         bus = event_bus._event_bus
         bus._subscriptions[mock_type] = [
-            EventSubscription(mock_type, lambda _: None)]
+            EventSubscription(mock_type, lambda _: None)
+        ]
         new_subscription = EventSubscription(mock_type, lambda _: True)
 
         reg_id = event_bus.register_subscription(new_subscription)
@@ -126,8 +126,10 @@
         """Tests posting with ignore_errors=True calls all registered funcs,
         even if they raise errors.
         """
+
         def _raise(_):
             raise Exception
+
         mock_event = Mock()
         mock_subscriptions = [Mock(), Mock(), Mock()]
         mock_subscriptions[0].deliver.side_effect = _raise
@@ -195,8 +197,8 @@
                          len(unregister_list_1) + len(unregister_list_2))
         for args, _ in unregister.call_args_list:
             subscription = args[0]
-            self.assertTrue(subscription in unregister_list_1 or
-                            subscription in unregister_list_2)
+            self.assertTrue(subscription in unregister_list_1
+                            or subscription in unregister_list_2)
 
     def test_unregister_given_an_event_subscription(self):
         """Tests that unregister can unregister a given EventSubscription."""
diff --git a/acts/framework/tests/libs/version_selector_test.py b/acts/framework/tests/libs/version_selector_test.py
index 499d5e9..5186c3e 100755
--- a/acts/framework/tests/libs/version_selector_test.py
+++ b/acts/framework/tests/libs/version_selector_test.py
@@ -21,7 +21,6 @@
 # A temporary hack to prevent tests/libs/logging from being selected as the
 # python default logging module.
 sys.path[0] = os.path.join(sys.path[0], '../')
-import logging
 import mock
 import shutil
 import tempfile
@@ -33,6 +32,7 @@
 
 from mobly.config_parser import TestRunConfig
 
+
 def versioning_decorator(min_sdk, max_sdk):
     return version_selector.set_version(lambda ret, *_, **__: ret, min_sdk,
                                         max_sdk)
@@ -106,6 +106,7 @@
 
 class VersionSelectorIntegrationTest(unittest.TestCase):
     """Tests the acts.libs.version_selector module."""
+
     @classmethod
     def setUpClass(cls):
         cls.tmp_dir = tempfile.mkdtemp()
diff --git a/acts/framework/tests/metrics/core_test.py b/acts/framework/tests/metrics/core_test.py
index ec9b6c1..ab59401 100644
--- a/acts/framework/tests/metrics/core_test.py
+++ b/acts/framework/tests/metrics/core_test.py
@@ -14,7 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from functools import partial
 from mock import call
 from mock import Mock
 from mock import patch
@@ -29,6 +28,7 @@
 DUMP_STRING_TO_FILE = 'acts.metrics.core.dump_string_to_file'
 MAKEDIRS = 'acts.metrics.core.os.makedirs'
 
+
 class ProtoMetricTest(TestCase):
     """Unit tests for the ProtoMetric class."""
 
@@ -36,10 +36,9 @@
 
     def setUp(self):
         self.data = Mock()
-        pass
 
     def test_default_init_attributes(self):
-        metric = ProtoMetric(name = self.TEST_NAME, data=self.data)
+        metric = ProtoMetric(name=self.TEST_NAME, data=self.data)
         self.assertEqual(metric.name, self.TEST_NAME)
         self.assertEqual(metric.data, self.data)
 
@@ -104,7 +103,8 @@
         metrics = Mock()
         publisher = MetricPublisher(context)
 
-        self.assertRaises(NotImplementedError, lambda: publisher.publish(metrics))
+        self.assertRaises(
+            NotImplementedError, lambda: publisher.publish(metrics))
 
 
 class ProtoMetricPublisherTest(TestCase):
@@ -161,12 +161,11 @@
         context = Mock()
         context.get_full_output_path.return_value = 'output/path'
         metrics = [Mock()]
-        publisher = ProtoMetricPublisher(
-            context,
-            publishes_binary=False,
-            publishes_ascii=False,
-            publishes_descriptor_binary=False,
-            publishes_descriptor_ascii=False)
+        publisher = ProtoMetricPublisher(context,
+                                         publishes_binary=False,
+                                         publishes_ascii=False,
+                                         publishes_descriptor_binary=False,
+                                         publishes_descriptor_ascii=False)
 
         publisher.publish(metrics)
 
@@ -179,16 +178,15 @@
         context = Mock()
         context.get_full_output_path.return_value = 'output/path'
         metrics = [Mock()]
-        publisher = ProtoMetricPublisher(
-            context,
-            publishes_binary=False,
-            publishes_ascii=False,
-            publishes_descriptor_binary=False,
-            publishes_descriptor_ascii=False)
+        publisher = ProtoMetricPublisher(context,
+                                         publishes_binary=False,
+                                         publishes_ascii=False,
+                                         publishes_descriptor_binary=False,
+                                         publishes_descriptor_ascii=False)
 
         publisher.publish(metrics)
 
-        metrics_output_path= 'output/path/metrics'
+        metrics_output_path = 'output/path/metrics'
         makedirs.assert_called_once_with(metrics_output_path, exist_ok=True)
 
     @patch(MAKEDIRS)
@@ -201,21 +199,19 @@
         binary = Mock()
         metric.get_binary = Mock(return_value=binary)
         metric.name = 'metric'
-        publisher = ProtoMetricPublisher(
-            context,
-            publishes_binary=True,
-            publishes_ascii=False,
-            publishes_descriptor_binary=False,
-            publishes_descriptor_ascii=False)
+        publisher = ProtoMetricPublisher(context,
+                                         publishes_binary=True,
+                                         publishes_ascii=False,
+                                         publishes_descriptor_binary=False,
+                                         publishes_descriptor_ascii=False)
 
         publisher.publish([metric])
 
         file_path = ('output/path/metrics/metric.' +
-                    ProtoMetricPublisher.BINARY_EXTENSION)
-        dump_string_to_file.assert_called_once_with(
-            binary,
-            file_path,
-            mode='wb')
+                     ProtoMetricPublisher.BINARY_EXTENSION)
+        dump_string_to_file.assert_called_once_with(binary,
+                                                    file_path,
+                                                    mode='wb')
 
     @patch(MAKEDIRS)
     @patch(DUMP_STRING_TO_FILE)
@@ -227,20 +223,17 @@
         ascii = Mock()
         metric.get_ascii = Mock(return_value=ascii)
         metric.name = 'metric'
-        publisher = ProtoMetricPublisher(
-            context,
-            publishes_binary=False,
-            publishes_ascii=True,
-            publishes_descriptor_binary=False,
-            publishes_descriptor_ascii=False)
+        publisher = ProtoMetricPublisher(context,
+                                         publishes_binary=False,
+                                         publishes_ascii=True,
+                                         publishes_descriptor_binary=False,
+                                         publishes_descriptor_ascii=False)
 
         publisher.publish([metric])
 
         file_path = ('output/path/metrics/metric.' +
-                    ProtoMetricPublisher.ASCII_EXTENSION)
-        dump_string_to_file.assert_called_once_with(
-            ascii,
-            file_path)
+                     ProtoMetricPublisher.ASCII_EXTENSION)
+        dump_string_to_file.assert_called_once_with(ascii, file_path)
 
     @patch(MAKEDIRS)
     @patch(DUMP_STRING_TO_FILE)
@@ -252,21 +245,19 @@
         descriptor_binary = Mock()
         metric.get_descriptor_binary = Mock(return_value=descriptor_binary)
         metric.name = 'metric'
-        publisher = ProtoMetricPublisher(
-            context,
-            publishes_binary=False,
-            publishes_ascii=False,
-            publishes_descriptor_binary=True,
-            publishes_descriptor_ascii=False)
+        publisher = ProtoMetricPublisher(context,
+                                         publishes_binary=False,
+                                         publishes_ascii=False,
+                                         publishes_descriptor_binary=True,
+                                         publishes_descriptor_ascii=False)
 
         publisher.publish([metric])
 
         file_path = ('output/path/metrics/metric.' +
-                    ProtoMetricPublisher.BINARY_DESCRIPTOR_EXTENSION)
-        dump_string_to_file.assert_called_once_with(
-            descriptor_binary,
-            file_path,
-            mode='wb')
+                     ProtoMetricPublisher.BINARY_DESCRIPTOR_EXTENSION)
+        dump_string_to_file.assert_called_once_with(descriptor_binary,
+                                                    file_path,
+                                                    mode='wb')
 
     @patch(MAKEDIRS)
     @patch(DUMP_STRING_TO_FILE)
@@ -278,20 +269,18 @@
         descriptor_ascii = Mock()
         metric.get_ascii = Mock(return_value=descriptor_ascii)
         metric.name = 'metric'
-        publisher = ProtoMetricPublisher(
-            context,
-            publishes_binary=False,
-            publishes_ascii=True,
-            publishes_descriptor_binary=False,
-            publishes_descriptor_ascii=False)
+        publisher = ProtoMetricPublisher(context,
+                                         publishes_binary=False,
+                                         publishes_ascii=True,
+                                         publishes_descriptor_binary=False,
+                                         publishes_descriptor_ascii=False)
 
         publisher.publish([metric])
 
         file_path = ('output/path/metrics/metric.' +
-                    ProtoMetricPublisher.ASCII_EXTENSION)
-        dump_string_to_file.assert_called_once_with(
-            descriptor_ascii,
-            file_path)
+                     ProtoMetricPublisher.ASCII_EXTENSION)
+        dump_string_to_file.assert_called_once_with(descriptor_ascii,
+                                                    file_path)
 
     @patch(MAKEDIRS)
     @patch(DUMP_STRING_TO_FILE)
@@ -307,19 +296,18 @@
         metric_2.get_binary = Mock(return_value=binary_2)
         metric_1.name = 'metric_1'
         metric_2.name = 'metric_2'
-        publisher = ProtoMetricPublisher(
-            context,
-            publishes_binary=True,
-            publishes_ascii=False,
-            publishes_descriptor_binary=False,
-            publishes_descriptor_ascii=False)
+        publisher = ProtoMetricPublisher(context,
+                                         publishes_binary=True,
+                                         publishes_ascii=False,
+                                         publishes_descriptor_binary=False,
+                                         publishes_descriptor_ascii=False)
 
         publisher.publish([metric_1, metric_2])
 
         file_path_1 = ('output/path/metrics/metric_1.' +
-                      ProtoMetricPublisher.BINARY_EXTENSION)
+                       ProtoMetricPublisher.BINARY_EXTENSION)
         file_path_2 = ('output/path/metrics/metric_2.' +
-                      ProtoMetricPublisher.BINARY_EXTENSION)
+                       ProtoMetricPublisher.BINARY_EXTENSION)
 
         call_1 = call(binary_1, file_path_1, mode='wb')
         call_2 = call(binary_2, file_path_2, mode='wb')
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/bluetooth_device.py b/acts_tests/acts_contrib/test_utils/abstract_devices/bluetooth_device.py
index da61712..712fbd9 100644
--- a/acts_tests/acts_contrib/test_utils/abstract_devices/bluetooth_device.py
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/bluetooth_device.py
@@ -58,6 +58,7 @@
     Attributes:
         device: A generic Bluetooth device.
     """
+
     def __init__(self, device):
         self.device = device
         self.log = logging
@@ -364,6 +365,7 @@
     Attributes:
         android_device: An Android Bluetooth device.
     """
+
     def __init__(self, android_device):
         super().__init__(android_device)
         self.gatt_timeout = 10
@@ -424,7 +426,6 @@
         """ Just pass for Android as there is no concept of initializing
         a Bluetooth controller.
         """
-        pass
 
     def start_pairing_helper(self):
         """ Starts the Android pairing helper.
@@ -759,7 +760,6 @@
             profile_id: The profile ID to set.
         """
         # Android devices currently have no hooks to modify the SDP record.
-        pass
 
     def sdp_add_service(self, sdp_record):
         """Adds an SDP service record.
@@ -770,18 +770,16 @@
                 None if failed.
         """
         # Android devices currently have no hooks to modify the SDP record.
-        pass
 
     def sdp_clean_up(self):
         """Cleans up all objects related to SDP.
         """
-        self.device.sdp_lib.cleanUp()
+        self.device.sl4f.sdp_lib.cleanUp()
 
     def sdp_init(self):
         """Initializes SDP on the device.
         """
         # Android devices currently have no hooks to modify the SDP record.
-        pass
 
     def sdp_remove_service(self, service_id):
         """Removes a service based on an input id.
@@ -789,7 +787,6 @@
             service_id: The service ID to remove.
         """
         # Android devices currently have no hooks to modify the SDP record.
-        pass
 
     def unbond_all_known_devices(self):
         """ Unbond all known remote devices.
@@ -833,6 +830,7 @@
     Attributes:
         fuchsia_device: A Fuchsia Bluetooth device.
     """
+
     def __init__(self, fuchsia_device):
         super().__init__(fuchsia_device)
 
@@ -843,19 +841,18 @@
     def start_profile_a2dp_sink(self):
         """ Starts the A2DP sink profile.
         """
-        self.device.control_daemon("bt-a2dp-sink.cmx", "start")
+        self.device.start_v1_component("bt-a2dp-sink")
 
     def stop_profile_a2dp_sink(self):
         """ Stops the A2DP sink profile.
         """
-        self.device.control_daemon("bt-a2dp-sink.cmx", "stop")
+        self.device.stop_v1_component("bt-a2dp-sink")
 
     def start_pairing_helper(self):
-        self.device.bts_lib.acceptPairing()
+        self.device.sl4f.bts_lib.acceptPairing()
 
     def bluetooth_toggle_state(self, state):
         """Stub for Fuchsia implementation."""
-        pass
 
     def set_discoverable(self, is_discoverable):
         """ Sets the device's discoverability.
@@ -863,12 +860,12 @@
         Args:
             is_discoverable: True if discoverable, false if not discoverable
         """
-        self.device.bts_lib.setDiscoverable(is_discoverable)
+        self.device.sl4f.bts_lib.setDiscoverable(is_discoverable)
 
     def get_pairing_pin(self):
         """ Get the pairing pin from the active pairing delegate.
         """
-        return self.device.bts_lib.getPairingPin()['result']
+        return self.device.sl4f.bts_lib.getPairingPin()['result']
 
     def input_pairing_pin(self, pin):
         """ Input pairing pin to active pairing delegate.
@@ -876,24 +873,24 @@
         Args:
             pin: The pin to input.
         """
-        self.device.bts_lib.inputPairingPin(pin)
+        self.device.sl4f.bts_lib.inputPairingPin(pin)
 
     def initialize_bluetooth_controller(self):
         """ Initialize Bluetooth controller for first time use.
         """
-        self.device.bts_lib.initBluetoothSys()
+        self.device.sl4f.bts_lib.initBluetoothSys()
 
     def get_local_bluetooth_address(self):
         """ Returns the Bluetooth local address.
         """
-        return self.device.bts_lib.getActiveAdapterAddress().get("result")
+        return self.device.sl4f.bts_lib.getActiveAdapterAddress().get("result")
 
     def set_bluetooth_local_name(self, name):
         """ Sets the Bluetooth controller's local name
         Args:
             name: The name to set.
         """
-        self.device.bts_lib.setName(name)
+        self.device.sl4f.bts_lib.setName(name)
 
     def gatt_client_write_characteristic_without_response_by_handle(
             self, peer_identifier, handle, value):
@@ -911,7 +908,7 @@
                 peer_identifier, handle)):
             self.log.warn(
                 "Unable to find handle {} in GATT server db.".format(handle))
-        result = self.device.gattc_lib.writeCharByIdWithoutResponse(
+        result = self.device.sl4f.gattc_lib.writeCharByIdWithoutResponse(
             handle, value)
         if result.get("error") is not None:
             self.log.error(
@@ -937,7 +934,8 @@
                 peer_identifier, handle)):
             self.log.warn(
                 "Unable to find handle {} in GATT server db.".format(handle))
-        result = self.device.gattc_lib.writeCharById(handle, offset, value)
+        result = self.device.sl4f.gattc_lib.writeCharById(
+            handle, offset, value)
         if result.get("error") is not None:
             self.log.error(
                 "Failed to write characteristic handle {} with err: {}".format(
@@ -964,7 +962,7 @@
             self.log.error(
                 "Unable to find handle {} in GATT server db.".format(handle))
             return False
-        result = self.device.gattc_lib.writeLongCharById(
+        result = self.device.sl4f.gattc_lib.writeLongCharById(
             handle, offset, value, reliable_mode)
         if result.get("error") is not None:
             self.log.error(
@@ -991,7 +989,8 @@
             self.log.error(
                 "Unable to find handle {} in GATT server db.".format(handle))
             return False
-        result = self.device.gattc_lib.writeLongDescById(handle, offset, value)
+        result = self.device.sl4f.gattc_lib.writeLongDescById(
+            handle, offset, value)
         if result.get("error") is not None:
             self.log.error(
                 "Failed to write long descriptor handle {} with err: {}".
@@ -1014,7 +1013,7 @@
                 peer_identifier, handle)):
             self.log.warn(
                 "Unable to find handle {} in GATT server db.".format(handle))
-        result = self.device.gattc_lib.readCharacteristicById(handle)
+        result = self.device.sl4f.gattc_lib.readCharacteristicById(handle)
         if result.get("error") is not None:
             self.log.error(
                 "Failed to read characteristic handle {} with err: {}".format(
@@ -1036,7 +1035,7 @@
                 peer_identifier, uuid, uuid=True)):
             self.log.warn(
                 "Unable to find uuid {} in GATT server db.".format(uuid))
-        result = self.device.gattc_lib.readCharacteristicByType(uuid)
+        result = self.device.sl4f.gattc_lib.readCharacteristicByType(uuid)
         if result.get("error") is not None:
             self.log.error(
                 "Failed to read characteristic uuid {} with err: {}".format(
@@ -1062,7 +1061,7 @@
                 peer_identifier, handle)):
             self.log.warn(
                 "Unable to find handle {} in GATT server db.".format(handle))
-        result = self.device.gattc_lib.readLongCharacteristicById(
+        result = self.device.sl4f.gattc_lib.readLongCharacteristicById(
             handle, offset, max_bytes)
         if result.get("error") is not None:
             self.log.error(
@@ -1086,7 +1085,7 @@
                 peer_identifier, handle)):
             self.log.warn(
                 "Unable to find handle {} in GATT server db.".format(handle))
-        result = self.device.gattc_lib.enableNotifyCharacteristic(handle)
+        result = self.device.sl4f.gattc_lib.enableNotifyCharacteristic(handle)
         if result.get("error") is not None:
             self.log.error(
                 "Failed to enable characteristic notifications for handle {} "
@@ -1109,7 +1108,7 @@
                 peer_identifier, handle)):
             self.log.warn(
                 "Unable to find handle {} in GATT server db.".format(handle))
-        result = self.device.gattc_lib.disableNotifyCharacteristic(handle)
+        result = self.device.sl4f.gattc_lib.disableNotifyCharacteristic(handle)
         if result.get("error") is not None:
             self.log.error(
                 "Failed to disable characteristic notifications for handle {} "
@@ -1131,7 +1130,7 @@
                 peer_identifier, handle)):
             self.log.warn(
                 "Unable to find handle {} in GATT server db.".format(handle))
-        result = self.device.gattc_lib.readDescriptorById(handle)
+        result = self.device.sl4f.gattc_lib.readDescriptorById(handle)
         if result.get("error") is not None:
             self.log.error(
                 "Failed to read descriptor for handle {} with err: {}".format(
@@ -1156,7 +1155,7 @@
                 peer_identifier, handle)):
             self.log.warn(
                 "Unable to find handle {} in GATT server db.".format(handle))
-        result = self.device.gattc_lib.writeDescriptorById(
+        result = self.device.sl4f.gattc_lib.writeDescriptorById(
             handle, offset, value)
         if result.get("error") is not None:
             self.log.error(
@@ -1175,7 +1174,7 @@
         Returns:
             True if success, False if failure.
         """
-        connection_result = self.device.gattc_lib.bleConnectToPeripheral(
+        connection_result = self.device.sl4f.gattc_lib.bleConnectToPeripheral(
             peer_identifier)
         if connection_result.get("error") is not None:
             self.log.error("Failed to connect to peer id {}: {}".format(
@@ -1218,7 +1217,7 @@
         Returns:
             True if success, False if failure.
         """
-        disconnect_result = self.device.gattc_lib.bleDisconnectPeripheral(
+        disconnect_result = self.device.sl4f.gattc_lib.bleDisconnectPeripheral(
             peer_identifier)
         if disconnect_result.get("error") is not None:
             self.log.error("Failed to disconnect from peer id {}: {}".format(
@@ -1228,7 +1227,6 @@
 
     def reset_bluetooth(self):
         """Stub for Fuchsia implementation."""
-        pass
 
     def sdp_add_search(self, attribute_list, profile_id):
         """Adds an SDP search record.
@@ -1236,31 +1234,31 @@
             attribute_list: The list of attributes to set
             profile_id: The profile ID to set.
         """
-        return self.device.sdp_lib.addSearch(attribute_list, profile_id)
+        return self.device.sl4f.sdp_lib.addSearch(attribute_list, profile_id)
 
     def sdp_add_service(self, sdp_record):
         """Adds an SDP service record.
         Args:
             sdp_record: The dictionary representing the search record to add.
         """
-        return self.device.sdp_lib.addService(sdp_record)
+        return self.device.sl4f.sdp_lib.addService(sdp_record)
 
     def sdp_clean_up(self):
         """Cleans up all objects related to SDP.
         """
-        return self.device.sdp_lib.cleanUp()
+        return self.device.sl4f.sdp_lib.cleanUp()
 
     def sdp_init(self):
         """Initializes SDP on the device.
         """
-        return self.device.sdp_lib.init()
+        return self.device.sl4f.sdp_lib.init()
 
     def sdp_remove_service(self, service_id):
         """Removes a service based on an input id.
         Args:
             service_id: The service ID to remove.
         """
-        return self.device.sdp_lib.init()
+        return self.device.sl4f.sdp_lib.init()
 
     def start_le_advertisement(self, adv_data, scan_response, adv_interval,
                                connectable):
@@ -1270,13 +1268,13 @@
             adv_data: Advertisement data.
             adv_interval: Advertisement interval.
         """
-        self.device.ble_lib.bleStartBleAdvertising(adv_data, scan_response,
-                                                   adv_interval, connectable)
+        self.device.sl4f.ble_lib.bleStartBleAdvertising(
+            adv_data, scan_response, adv_interval, connectable)
 
     def stop_le_advertisement(self):
         """ Stop active LE advertisement.
         """
-        self.device.ble_lib.bleStopBleAdvertising()
+        self.device.sl4f.ble_lib.bleStopBleAdvertising()
 
     def setup_gatt_server(self, database):
         """ Sets up an input GATT server.
@@ -1284,12 +1282,12 @@
         Args:
             database: A dictionary representing the GATT database to setup.
         """
-        self.device.gatts_lib.publishServer(database)
+        self.device.sl4f.gatts_lib.publishServer(database)
 
     def close_gatt_server(self):
         """ Closes an existing GATT server.
         """
-        self.device.gatts_lib.closeServer()
+        self.device.sl4f.gatts_lib.closeServer()
 
     def le_scan_with_name_filter(self, name, timeout):
         """ Scan over LE for a specific device name.
@@ -1310,18 +1308,18 @@
         Args:
             log: The informative log.
         """
-        self.device.logging_lib.logI(log)
-        pass
+        self.device.sl4f.logging_lib.logI(log)
 
     def unbond_all_known_devices(self):
         """ Unbond all known remote devices.
         """
         try:
-            device_list = self.device.bts_lib.getKnownRemoteDevices()['result']
+            device_list = self.device.sl4f.bts_lib.getKnownRemoteDevices(
+            )['result']
             for device_info in device_list:
                 device = device_list[device_info]
                 if device['bonded']:
-                    self.device.bts_lib.forgetDevice(device['id'])
+                    self.device.sl4f.bts_lib.forgetDevice(device['id'])
         except Exception as err:
             self.log.err("Unable to unbond all devices: {}".format(err))
 
@@ -1332,7 +1330,7 @@
             peer_identifier: The peer identifier for the peer to unbond.
 
         """
-        self.device.bts_lib.forgetDevice(peer_identifier)
+        self.device.sl4f.bts_lib.forgetDevice(peer_identifier)
 
     def _find_service_id_and_connect_to_service_for_handle(
             self, peer_identifier, handle, uuid=False):
@@ -1340,12 +1338,12 @@
         if uuid:
             handle = handle.lower()
         try:
-            services = self.device.gattc_lib.listServices(peer_identifier)
+            services = self.device.sl4f.gattc_lib.listServices(peer_identifier)
             for service in services['result']:
                 service_id = service['id']
-                self.device.gattc_lib.connectToService(peer_identifier,
-                                                       service_id)
-                chars = self.device.gattc_lib.discoverCharacteristics()
+                self.device.sl4f.gattc_lib.connectToService(
+                    peer_identifier, service_id)
+                chars = self.device.sl4f.gattc_lib.discoverCharacteristics()
 
                 for char in chars['result']:
                     char_id = char['id']
@@ -1367,13 +1365,13 @@
     def _read_all_characteristics(self, peer_identifier, uuid=None):
         fail_err = "Failed to read all characteristics with: {}"
         try:
-            services = self.device.gattc_lib.listServices(peer_identifier)
+            services = self.device.sl4f.gattc_lib.listServices(peer_identifier)
             for service in services['result']:
                 service_id = service['id']
                 service_uuid = service['uuid_type']
-                self.device.gattc_lib.connectToService(peer_identifier,
-                                                       service_id)
-                chars = self.device.gattc_lib.discoverCharacteristics()
+                self.device.sl4f.gattc_lib.connectToService(
+                    peer_identifier, service_id)
+                chars = self.device.sl4f.gattc_lib.discoverCharacteristics()
                 self.log.info(
                     "Reading chars in service uuid: {}".format(service_uuid))
 
@@ -1384,7 +1382,7 @@
                         continue
                     try:
                         read_val =  \
-                            self.device.gattc_lib.readCharacteristicById(
+                            self.device.sl4f.gattc_lib.readCharacteristicById(
                                 char_id)
                         self.log.info(
                             "\tCharacteristic uuid / Value: {} / {}".format(
@@ -1395,20 +1393,19 @@
                         self.log.info("\t\tstr val: {}".format(str_value))
                     except Exception as err:
                         self.log.error(err)
-                        pass
         except Exception as err:
             self.log.error(fail_err.forma(err))
 
     def _perform_read_all_descriptors(self, peer_identifier):
         fail_err = "Failed to read all characteristics with: {}"
         try:
-            services = self.device.gattc_lib.listServices(peer_identifier)
+            services = self.device.sl4f.gattc_lib.listServices(peer_identifier)
             for service in services['result']:
                 service_id = service['id']
                 service_uuid = service['uuid_type']
-                self.device.gattc_lib.connectToService(peer_identifier,
-                                                       service_id)
-                chars = self.device.gattc_lib.discoverCharacteristics()
+                self.device.sl4f.gattc_lib.connectToService(
+                    peer_identifier, service_id)
+                chars = self.device.sl4f.gattc_lib.discoverCharacteristics()
                 self.log.info(
                     "Reading descs in service uuid: {}".format(service_uuid))
 
@@ -1422,7 +1419,7 @@
                         desc_id = desc["id"]
                         desc_uuid = desc["uuid_type"]
                     try:
-                        read_val = self.device.gattc_lib.readDescriptorById(
+                        read_val = self.device.sl4f.gattc_lib.readDescriptorById(
                             desc_id)
                         self.log.info(
                             "\t\tDescriptor uuid / Value: {} / {}".format(
@@ -1462,8 +1459,8 @@
             True if successful, False if failed.
         """
         try:
-            self.device.bts_lib.pair(peer_identifier, security_level,
-                                     non_bondable, transport)
+            self.device.sl4f.bts_lib.pair(peer_identifier, security_level,
+                                          non_bondable, transport)
             return True
         except Exception as err:
             fail_err = "Failed to pair to peer_identifier {} with: {}".format(
diff --git a/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device.py b/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device.py
index f2742e0..370ec03 100644
--- a/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device.py
+++ b/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device.py
@@ -16,9 +16,9 @@
 
 import inspect
 import logging
+import time
 
 import acts_contrib.test_utils.wifi.wifi_test_utils as awutils
-from acts.utils import get_interface_ip_addresses
 from acts.utils import adb_shell_ping
 
 from acts import asserts
@@ -26,6 +26,8 @@
 from acts.controllers.fuchsia_device import FuchsiaDevice
 from acts.controllers.android_device import AndroidDevice
 
+FUCHSIA_VALID_SECURITY_TYPES = {"none", "wep", "wpa", "wpa2", "wpa3"}
+
 
 def create_wlan_device(hardware_device):
     """Creates a generic WLAN device based on type of device that is sent to
@@ -43,9 +45,6 @@
                          type(hardware_device))
 
 
-FUCHSIA_VALID_SECURITY_TYPES = {"none", "wep", "wpa", "wpa2", "wpa3"}
-
-
 class WlanDevice(object):
     """Class representing a generic WLAN device.
 
@@ -137,10 +136,6 @@
         raise NotImplementedError("{} must be defined.".format(
             inspect.currentframe().f_code.co_name))
 
-    def get_interface_ip_addresses(self, interface):
-        raise NotImplementedError("{} must be defined.".format(
-            inspect.currentframe().f_code.co_name))
-
     def is_connected(self, ssid=None):
         raise NotImplementedError("{} must be defined.".format(
             inspect.currentframe().f_code.co_name))
@@ -261,9 +256,6 @@
     def send_command(self, command):
         return self.device.adb.shell(str(command))
 
-    def get_interface_ip_addresses(self, interface):
-        return get_interface_ip_addresses(self.device, interface)
-
     def is_connected(self, ssid=None):
         wifi_info = self.device.droid.wifiGetConnectionInfo()
         if ssid:
@@ -329,11 +321,9 @@
 
     def wifi_toggle_state(self, state):
         """Stub for Fuchsia implementation."""
-        pass
 
     def reset_wifi(self):
         """Stub for Fuchsia implementation."""
-        pass
 
     def take_bug_report(self, test_name=None, begin_time=None):
         """Stub for Fuchsia implementation."""
@@ -341,11 +331,9 @@
 
     def get_log(self, test_name, begin_time):
         """Stub for Fuchsia implementation."""
-        pass
 
     def turn_location_off_and_scan_toggle_off(self):
         """Stub for Fuchsia implementation."""
-        pass
 
     def associate(self,
                   target_ssid,
@@ -368,7 +356,7 @@
             True if successfully connected to WLAN, False if not.
         """
         if self.device.association_mechanism == 'drivers':
-            bss_scan_response = self.device.wlan_lib.wlanScanForBSSInfo()
+            bss_scan_response = self.device.sl4f.wlan_lib.wlanScanForBSSInfo()
             if bss_scan_response.get('error'):
                 self.log.error('Scan for BSS info failed. Err: %s' %
                                bss_scan_response['error'])
@@ -382,7 +370,7 @@
                     % target_ssid)
                 return False
 
-            connection_response = self.device.wlan_lib.wlanConnectToNetwork(
+            connection_response = self.device.sl4f.wlan_lib.wlanConnectToNetwork(
                 target_ssid, bss_descs_for_ssid[0], target_pwd=target_pwd)
             return self.device.check_connect_response(connection_response)
         else:
@@ -394,14 +382,14 @@
            Asserts if disconnect was not successful.
         """
         if self.device.association_mechanism == 'drivers':
-            disconnect_response = self.device.wlan_lib.wlanDisconnect()
+            disconnect_response = self.device.sl4f.wlan_lib.wlanDisconnect()
             return self.device.check_disconnect_response(disconnect_response)
         else:
             return self.device.wlan_policy_controller.remove_all_networks_and_wait_for_no_connections(
             )
 
     def status(self):
-        return self.device.wlan_lib.wlanStatus()
+        return self.device.sl4f.wlan_lib.wlanStatus()
 
     def can_ping(self,
                  dest_ip,
@@ -438,7 +426,7 @@
         Returns:
             A list of wlan interface IDs.
         """
-        return self.device.wlan_lib.wlanGetIfaceIdList().get('result')
+        return self.device.sl4f.wlan_lib.wlanGetIfaceIdList().get('result')
 
     def get_default_wlan_test_interface(self):
         """Returns name of the WLAN client interface"""
@@ -455,7 +443,7 @@
         Returns:
             True if successfully destroyed wlan interface, False if not.
         """
-        result = self.device.wlan_lib.wlanDestroyIface(iface_id)
+        result = self.device.sl4f.wlan_lib.wlanDestroyIface(iface_id)
         if result.get('error') is None:
             return True
         else:
@@ -464,10 +452,7 @@
             return False
 
     def send_command(self, command):
-        return self.device.send_command_ssh(str(command)).stdout
-
-    def get_interface_ip_addresses(self, interface):
-        return get_interface_ip_addresses(self.device, interface)
+        return self.device.ssh.run(str(command)).stdout
 
     def is_connected(self, ssid=None):
         """ Determines if wlan_device is connected to wlan network.
@@ -541,11 +526,16 @@
         """
         if not test_interface:
             test_interface = self.get_default_wlan_test_interface()
+
+        # A package server is necessary to acquire the iperf3 client for
+        # some builds.
+        self.device.start_package_server()
+
         return iperf_client.IPerfClientOverSsh(
             {
                 'user': 'fuchsia',
                 'host': self.device.ip,
                 'ssh_config': self.device.ssh_config
             },
-            use_paramiko=True,
+            ssh_provider=self.device.ssh,
             test_interface=test_interface)
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
deleted file mode 100644
index 0546bad..0000000
--- a/acts_tests/acts_contrib/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright (C) 2020 The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT 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
-from mobly.base_test import STAGE_NAME_TEARDOWN_CLASS
-
-
-class AbstractDeviceWlanDeviceBaseTest(WifiBaseTest):
-    def setup_class(self):
-        super().setup_class()
-
-    def teardown_class(self):
-        begin_time = utils.get_current_epoch_time()
-        super().teardown_class()
-        for device in getattr(self, "android_devices", []):
-            device.take_bug_report(STAGE_NAME_TEARDOWN_CLASS, begin_time)
-        for device in getattr(self, "fuchsia_devices", []):
-            device.take_bug_report(STAGE_NAME_TEARDOWN_CLASS, begin_time)
-
-    def on_fail(self, test_name, begin_time):
-        """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)
-
-    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 = self.access_point.get_dhcp_logs()
-        if dhcp_log:
-            dhcp_log_file = open(dhcp_full_out_path, 'w')
-            dhcp_log_file.write(dhcp_log)
-            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/audio_analysis_lib/audio_analysis.py b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_analysis.py
index a5b9fe1..b2ae45c 100644
--- a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_analysis.py
+++ b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_analysis.py
@@ -50,12 +50,10 @@
 
 class RMSTooSmallError(Exception):
     """Error when signal RMS is too small."""
-    pass
 
 
 class EmptyDataError(Exception):
     """Error when signal is empty."""
-    pass
 
 
 def normalize_signal(signal, saturate_value):
@@ -234,8 +232,8 @@
         # Check the right half window and also record next candidate.
         # Favor the larger index for next_peak_candidate_index.
         for index in range(int(right), int(mid), -1):
-            if (next_peak_candidate_index is None or
-                    array[index] > array[next_peak_candidate_index]):
+            if (next_peak_candidate_index is None
+                    or array[index] > array[next_peak_candidate_index]):
                 next_peak_candidate_index = index
 
         if array[next_peak_candidate_index] >= value_mid:
@@ -355,14 +353,14 @@
         return bounds
     end = anoms[0]
     start = anoms[0]
-    for i in range(len(anoms)-1):
+    for i in range(len(anoms) - 1):
         end = anoms[i]
-        sample_diff = abs(anoms[i] - anoms[i+1]) * rate
+        sample_diff = abs(anoms[i] - anoms[i + 1]) * rate
         # We require a tolerance because sample_diff may be slightly off due to
         # float rounding errors in Python.
         if sample_diff > block_size / 2 + tolerance:
             bounds.append((start, end))
-            start = anoms[i+1]
+            start = anoms[i + 1]
     bounds.append((start, end))
     return bounds
 
@@ -393,8 +391,9 @@
     """
     samples_in_a_period = int(rate / freq) + 1
     samples_in_golden_pattern = samples_in_a_period + block_size
-    golden_x = numpy.linspace(0.0, (samples_in_golden_pattern - 1) * 1.0 /
-                              rate, samples_in_golden_pattern)
+    golden_x = numpy.linspace(0.0,
+                              (samples_in_golden_pattern - 1) * 1.0 / rate,
+                              samples_in_golden_pattern)
     golden_y = numpy.sin(freq * 2.0 * numpy.pi * golden_x)
     return golden_y
 
@@ -448,12 +447,10 @@
 
 class GoldenSignalNormTooSmallError(Exception):
     """Exception when golden signal norm is too small."""
-    pass
 
 
 class TestSignalNormTooSmallError(Exception):
     """Exception when test signal norm is too small."""
-    pass
 
 
 def _get_correlation_index(golden_signal, test_signal):
@@ -574,16 +571,18 @@
     audio_file = soundfile.SoundFile(filename)
     channel_results = []
     if audio_file.channels == 1:
-        channel_results.append(THDN(signal=audio_file.read(),
-                                    rate=audio_file.samplerate,
-                                    q=q,
-                                    freq=freq))
+        channel_results.append(
+            THDN(signal=audio_file.read(),
+                 rate=audio_file.samplerate,
+                 q=q,
+                 freq=freq))
     else:
         for ch_no, channel in enumerate(audio_file.read().transpose()):
-            channel_results.append(THDN(signal=channel,
-                                        rate=audio_file.samplerate,
-                                        q=q,
-                                        freq=freq))
+            channel_results.append(
+                THDN(signal=channel,
+                     rate=audio_file.samplerate,
+                     q=q,
+                     freq=freq))
     return channel_results
 
 
@@ -605,24 +604,27 @@
     audio_file = soundfile.SoundFile(filename)
     channel_results = []
     if audio_file.channels == 1:
-        channel_results.append(max_THDN(signal=audio_file.read(),
-                                        rate=audio_file.samplerate,
-                                        step_size=step_size,
-                                        window_size=window_size,
-                                        q=q,
-                                        freq=freq))
+        channel_results.append(
+            max_THDN(signal=audio_file.read(),
+                     rate=audio_file.samplerate,
+                     step_size=step_size,
+                     window_size=window_size,
+                     q=q,
+                     freq=freq))
     else:
         for ch_no, channel in enumerate(audio_file.read().transpose()):
-            channel_results.append(max_THDN(signal=channel,
-                                            rate=audio_file.samplerate,
-                                            step_size=step_size,
-                                            window_size=window_size,
-                                            q=q,
-                                            freq=freq))
+            channel_results.append(
+                max_THDN(signal=channel,
+                         rate=audio_file.samplerate,
+                         step_size=step_size,
+                         window_size=window_size,
+                         q=q,
+                         freq=freq))
     return channel_results
 
 
-def get_file_anomaly_durations(filename, freq=None,
+def get_file_anomaly_durations(filename,
+                               freq=None,
                                block_size=ANOMALY_DETECTION_BLOCK_SIZE,
                                threshold=PATTERN_MATCHING_THRESHOLD,
                                tolerance=ANOMALY_GROUPING_TOLERANCE):
@@ -648,20 +650,20 @@
     freq = freq or fundamental_freq(signal, audio_file.samplerate)
     channel_results = []
     if audio_file.channels == 1:
-        channel_results.append(get_anomaly_durations(
-            signal=signal,
-            rate=audio_file.samplerate,
-            freq=freq,
-            block_size=block_size,
-            threshold=threshold,
-            tolerance=tolerance))
+        channel_results.append(
+            get_anomaly_durations(signal=signal,
+                                  rate=audio_file.samplerate,
+                                  freq=freq,
+                                  block_size=block_size,
+                                  threshold=threshold,
+                                  tolerance=tolerance))
     else:
         for ch_no, channel in enumerate(signal.transpose()):
-            channel_results.append(get_anomaly_durations(
-                signal=channel,
-                rate=audio_file.samplerate,
-                freq=freq,
-                block_size=block_size,
-                threshold=threshold,
-                tolerance=tolerance))
+            channel_results.append(
+                get_anomaly_durations(signal=channel,
+                                      rate=audio_file.samplerate,
+                                      freq=freq,
+                                      block_size=block_size,
+                                      threshold=threshold,
+                                      tolerance=tolerance))
     return channel_results
diff --git a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_data.py b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_data.py
index 5867a48..cb1366a 100644
--- a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_data.py
+++ b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_data.py
@@ -15,11 +15,7 @@
 #   limitations under the License.
 """This module provides abstraction of audio data."""
 
-import contextlib
-import copy
 import numpy
-import struct
-from io import StringIO
 """The dict containing information on how to parse sample from raw data.
 
 Keys: The sample format as in aplay command.
@@ -31,14 +27,12 @@
     size_bytes: Number of bytes for one sample.
 """
 SAMPLE_FORMATS = dict(
-    S32_LE=dict(
-        message='Signed 32-bit integer, little-endian',
-        dtype_str='<i',
-        size_bytes=4),
-    S16_LE=dict(
-        message='Signed 16-bit integer, little-endian',
-        dtype_str='<i',
-        size_bytes=2))
+    S32_LE=dict(message='Signed 32-bit integer, little-endian',
+                dtype_str='<i',
+                size_bytes=4),
+    S16_LE=dict(message='Signed 16-bit integer, little-endian',
+                dtype_str='<i',
+                size_bytes=2))
 
 
 def get_maximum_value_from_sample_format(sample_format):
@@ -56,7 +50,6 @@
 
 class AudioRawDataError(Exception):
     """Error in AudioRawData."""
-    pass
 
 
 class AudioRawData(object):
diff --git a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_quality_measurement.py b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_quality_measurement.py
index c241fde..c50e738 100644
--- a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_quality_measurement.py
+++ b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/audio_quality_measurement.py
@@ -142,7 +142,6 @@
 
 class SineWaveNotFound(Exception):
     """Error when there's no sine wave found in the signal"""
-    pass
 
 
 def hilbert(x):
@@ -463,8 +462,8 @@
     for index in range(0, length):
         # Ignore noise too close to the beginning or the end of sine wave.
         # Check the docstring of NEAR_SINE_START_OR_END_SECS.
-        if ((start_index - rate * NEAR_SINE_START_OR_END_SECS) <= index and
-            (index < end_index + rate * NEAR_SINE_START_OR_END_SECS)):
+        if ((start_index - rate * NEAR_SINE_START_OR_END_SECS) <= index
+                and (index < end_index + rate * NEAR_SINE_START_OR_END_SECS)):
             continue
 
         # Ignore noise too close to the beginning or the end of original data.
@@ -478,8 +477,8 @@
         if block_amplitude[index] > amplitude_threshold:
             same_event = False
             if previous_noise_index:
-                same_event = (index - previous_noise_index
-                              ) < same_event_samples
+                same_event = (index -
+                              previous_noise_index) < same_event_samples
             if not same_event:
                 index_start_sec = float(index) / rate - APPEND_ZEROS_SECS
                 index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
@@ -560,8 +559,7 @@
         # If amplitude less than its left/right side and small enough,
         # it will be considered as a delay.
         amp_threshold = average_amplitude * delay_amplitude_threshold
-        left_threshold = delay_amplitude_threshold * left_block_amplitude[
-            index]
+        left_threshold = delay_amplitude_threshold * left_block_amplitude[index]
         amp_threshold = min(amp_threshold, left_threshold)
         right_threshold = delay_amplitude_threshold * right_block_amplitude[
             index]
@@ -575,8 +573,8 @@
         if amplitude_too_small or frequency_not_match:
             same_event = False
             if previous_delay_index:
-                same_event = (index - previous_delay_index
-                              ) < same_event_samples
+                same_event = (index -
+                              previous_delay_index) < same_event_samples
             if not same_event:
                 index_start_sec = float(index) / rate - APPEND_ZEROS_SECS
                 index_end_sec = float(index + 1) / rate - APPEND_ZEROS_SECS
@@ -646,8 +644,7 @@
         if abs(index - end_index) < rate * NEAR_START_OR_END_SECS:
             continue
         amp_threshold = average_amplitude * DEFAULT_BURST_TOO_SMALL
-        left_threshold = burst_amplitude_threshold * left_block_amplitude[
-            index]
+        left_threshold = burst_amplitude_threshold * left_block_amplitude[index]
         amp_threshold = max(amp_threshold, left_threshold)
         right_threshold = burst_amplitude_threshold * right_block_amplitude[
             index]
@@ -870,9 +867,10 @@
         frequency_delta, block_size * 2, block_size)
 
     # Finds start and end index of sine wave.
-    start_index, end_index = find_start_end_index(
-        dominant_frequency, block_frequency_delta, block_size,
-        frequency_error_threshold)
+    start_index, end_index = find_start_end_index(dominant_frequency,
+                                                  block_frequency_delta,
+                                                  block_size,
+                                                  frequency_error_threshold)
 
     if start_index > end_index:
         raise SineWaveNotFound('No sine wave found in signal')
diff --git a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/check_quality.py b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/check_quality.py
index ad41759..6c6c6f4 100644
--- a/acts_tests/acts_contrib/test_utils/audio_analysis_lib/check_quality.py
+++ b/acts_tests/acts_contrib/test_utils/audio_analysis_lib/check_quality.py
@@ -15,13 +15,10 @@
 #   limitations under the License.
 """Audio Analysis tool to analyze wave file and detect artifacts."""
 
-import argparse
 import collections
 import json
 import logging
-import math
 import numpy
-import os
 import pprint
 import subprocess
 import tempfile
@@ -48,12 +45,10 @@
 
 class WaveFileException(Exception):
     """Error in WaveFile."""
-    pass
 
 
 class WaveFormatExtensibleException(Exception):
     """Wave file is in WAVE_FORMAT_EXTENSIBLE format which is not supported."""
-    pass
 
 
 class WaveFile(object):
@@ -166,25 +161,21 @@
         """Reads in samples in wave file."""
         self._binary = self._wave_reader.readframes(self._n_frames)
         format_str = 'S%d_LE' % self._sample_width_bits
-        self.raw_data = audio_data.AudioRawData(
-            binary=self._binary,
-            channel=self._n_channels,
-            sample_format=format_str)
+        self.raw_data = audio_data.AudioRawData(binary=self._binary,
+                                                channel=self._n_channels,
+                                                sample_format=format_str)
 
 
 class QualityCheckerError(Exception):
     """Error in QualityChecker."""
-    pass
 
 
 class CompareFailure(QualityCheckerError):
     """Exception when frequency comparison fails."""
-    pass
 
 
 class QualityFailure(QualityCheckerError):
     """Exception when quality check fails."""
-    pass
 
 
 class QualityChecker(object):
@@ -241,9 +232,10 @@
             # Ignore high frequencies above the threshold.
             spectral = [(f, c) for (f, c) in spectral if f < ignore_high_freq]
 
-            logging.info('Channel %d spectral after ignoring high frequencies '
-                         'above %f:\n%s', channel_idx, ignore_high_freq,
-                         pprint.pformat(spectral))
+            logging.info(
+                'Channel %d spectral after ignoring high frequencies '
+                'above %f:\n%s', channel_idx, ignore_high_freq,
+                pprint.pformat(spectral))
 
             try:
                 if check_quality:
@@ -423,7 +415,6 @@
 
 class CheckQualityError(Exception):
     """Error in check_quality main function."""
-    pass
 
 
 def read_audio_file(filename, channel, bit_width, rate):
@@ -450,19 +441,21 @@
         binary = None
         with open(filename, 'rb') as f:
             binary = f.read()
-        raw_data = audio_data.AudioRawData(
-            binary=binary, channel=channel, sample_format='S%d_LE' % bit_width)
+        raw_data = audio_data.AudioRawData(binary=binary,
+                                           channel=channel,
+                                           sample_format='S%d_LE' % bit_width)
     else:
-        raise CheckQualityError(
-            'File format for %s is not supported' % filename)
+        raise CheckQualityError('File format for %s is not supported' %
+                                filename)
 
     return raw_data, rate
 
 
-def get_quality_params(
-        quality_block_size_secs, quality_frequency_error_threshold,
-        quality_delay_amplitude_threshold, quality_noise_amplitude_threshold,
-        quality_burst_amplitude_threshold):
+def get_quality_params(quality_block_size_secs,
+                       quality_frequency_error_threshold,
+                       quality_delay_amplitude_threshold,
+                       quality_noise_amplitude_threshold,
+                       quality_burst_amplitude_threshold):
     """Gets quality parameters in arguments.
 
     Args:
@@ -535,15 +528,15 @@
 
     checker = QualityChecker(raw_data, rate)
 
-    quality_params = get_quality_params(
-        quality_block_size_secs, quality_frequency_error_threshold,
-        quality_delay_amplitude_threshold, quality_noise_amplitude_threshold,
-        quality_burst_amplitude_threshold)
+    quality_params = get_quality_params(quality_block_size_secs,
+                                        quality_frequency_error_threshold,
+                                        quality_delay_amplitude_threshold,
+                                        quality_noise_amplitude_threshold,
+                                        quality_burst_amplitude_threshold)
 
-    checker.do_spectral_analysis(
-        ignore_high_freq=ignore_high_freq,
-        check_quality=(not spectral_only),
-        quality_params=quality_params)
+    checker.do_spectral_analysis(ignore_high_freq=ignore_high_freq,
+                                 check_quality=(not spectral_only),
+                                 quality_params=quality_params)
 
     checker.dump(output_file)
 
diff --git a/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py
index 1c20301..e39b4f4 100644
--- a/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/bt/BluetoothCarHfpBaseTest.py
@@ -20,7 +20,6 @@
 
 import os
 import time
-from queue import Empty
 
 from acts.keys import Config
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
@@ -100,8 +99,8 @@
 
     def on_fail(self, test_name, begin_time):
         result = True
-        if not super(BluetoothCarHfpBaseTest, self).on_fail(test_name,
-                                                            begin_time):
+        if not super(BluetoothCarHfpBaseTest, self).on_fail(
+                test_name, begin_time):
             result = False
         ensure_phones_default_state(self.log, self.android_devices[1:])
         return result
diff --git a/acts_tests/acts_contrib/test_utils/bt/BtFunhausBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BtFunhausBaseTest.py
index 127289e..b33831b 100644
--- a/acts_tests/acts_contrib/test_utils/bt/BtFunhausBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/bt/BtFunhausBaseTest.py
@@ -22,7 +22,6 @@
 from acts.utils import bypass_setup_wizard
 from acts.utils import exe_cmd
 from acts.utils import sync_device_time
-import json
 import time
 import os
 
diff --git a/acts_tests/acts_contrib/test_utils/bt/BtInterferenceBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BtInterferenceBaseTest.py
index 99ca5da..1251cd8 100644
--- a/acts_tests/acts_contrib/test_utils/bt/BtInterferenceBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/bt/BtInterferenceBaseTest.py
@@ -18,7 +18,6 @@
 
 import json
 import math
-import random
 import time
 import logging
 import acts.controllers.iperf_client as ipc
diff --git a/acts_tests/acts_contrib/test_utils/bt/BtMultiprofileBaseTest.py b/acts_tests/acts_contrib/test_utils/bt/BtMultiprofileBaseTest.py
new file mode 100644
index 0000000..f2246df
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/bt/BtMultiprofileBaseTest.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# 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.A2dpBaseTest import A2dpBaseTest
+import acts_contrib.test_utils.bt.BleBaseTest as BleBT
+from acts_contrib.test_utils.power.IperfHelper import IperfHelper
+from acts_contrib.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
+from concurrent.futures import ThreadPoolExecutor
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils
+
+
+class BtMultiprofileBaseTest(A2dpBaseTest, BleBT.BleBaseTest):
+    """Base class for BT mutiprofile related tests.
+
+     Inherited from the A2DP Base class, Ble Base class
+     """
+    # Iperf waiting time (margin)
+    IPERF_MARGIN = 10
+
+    def mutiprofile_test(self,
+                         codec_config=None,
+                         mode=None,
+                         victim=None,
+                         aggressor=None,
+                         metric=None):
+
+        if victim == 'A2DP' and aggressor == 'Ble_Scan' and metric == 'range':
+            scan_callback = self.start_ble_scan(self.dut, mode)
+            self.run_a2dp_to_max_range(codec_config)
+            self.dut.droid.bleStopBleScan(scan_callback)
+            self.log.info("BLE Scan stopped successfully")
+            return True
+
+        if victim == 'Ble_Scan' and aggressor == 'A2DP' and metric == 'scan_accuracy':
+            scan_callback = self.start_ble_scan(self.dut, mode)
+            recorded_file = self.play_and_record_audio(
+                self.audio_params['duration'])
+            self.dut.droid.bleStopBleScan(scan_callback)
+            self.media.stop()
+            self.log.info("BLE Scan & A2DP streaming stopped successfully")
+            return True
+
+        if victim == 'RFCOMM' and aggressor == 'Ble_Scan' and metric == 'throughput':
+            self.remote_device = self.android_devices[2]
+            scan_callback = self.start_ble_scan(self.dut, mode)
+            if not orchestrate_rfcomm_connection(self.dut, self.remote_device):
+                return False
+            self.log.info("RFCOMM Connection established")
+            self.measure_rfcomm_throughput(100)
+            self.dut.droid.bleStopBleScan(scan_callback)
+            self.log.info("BLE Scan stopped successfully")
+
+        if victim == 'A2DP' and aggressor == 'Ble_Adv' and metric == 'range':
+            advertise_callback = self.start_ble_adv(self.dut, mode, 2)
+            self.run_a2dp_to_max_range(codec_config)
+            self.dut.droid.bleStopBleAdvertising(advertise_callback)
+            self.log.info("Advertisement stopped Successfully")
+            return True
+
+        if victim == 'A2DP' and aggressor == 'Ble_conn' and metric == 'range':
+            self.start_ble_connection(self.dut, self.android_devices[2], mode)
+            self.run_a2dp_to_max_range(codec_config)
+            return True
+
+        if victim == 'A2DP' and aggressor == 'wifi' and metric == 'range':
+            self.setup_hotspot_and_connect_client()
+            self.setup_iperf_and_run_throughput()
+            self.run_a2dp_to_max_range(codec_config)
+            self.process_iperf_results()
+            return True
+
+        if victim == 'Ble_Scan' and aggressor == 'wifi' and metric == 'scan_accuracy':
+            scan_callback = self.start_ble_scan(self.dut, mode)
+            self.setup_hotspot_and_connect_client()
+            self.setup_iperf_and_run_throughput()
+            time.sleep(self.audio_params['duration'] + self.IPERF_MARGIN + 2)
+            self.log.info("BLE Scan & iPerf started successfully")
+            self.process_iperf_results()
+            self.dut.droid.bleStopBleScan(scan_callback)
+            self.log.info("BLE Scan stopped successfully")
+            return True
+
+        if victim == 'Ble_Adv' and aggressor == 'wifi' and metric == 'periodic_adv':
+            advertise_callback = self.start_ble_adv(self.dut, mode, 2)
+            self.setup_hotspot_and_connect_client()
+            self.setup_iperf_and_run_throughput()
+            time.sleep(self.audio_params['duration'] + self.IPERF_MARGIN + 2)
+            self.log.info("BLE Advertisement & iPerf started successfully")
+            self.process_iperf_results()
+            self.dut.droid.bleStopBleAdvertising(advertise_callback)
+            self.log.info("Advertisement stopped Successfully")
+            return True
+
+        if victim == 'RFCOMM' and aggressor == 'wifi' and metric == 'throughput':
+            self.remote_device = self.android_devices[2]
+            if not orchestrate_rfcomm_connection(self.dut, self.remote_device):
+                return False
+            self.log.info("RFCOMM Connection established")
+            self.setup_hotspot_and_connect_client()
+            executor = ThreadPoolExecutor(2)
+            throughput = executor.submit(self.measure_rfcomm_throughput, 100)
+            executor.submit(self.setup_iperf_and_run_throughput, )
+            time.sleep(self.audio_params['duration'] + self.IPERF_MARGIN + 10)
+            self.process_iperf_results()
+            return True
+
+    def measure_rfcomm_throughput(self, iteration):
+        """Measures the throughput of a data transfer.
+
+        Sends data over RFCOMM from the client device that is read by the server device.
+        Calculates the throughput for the transfer.
+
+        Args:
+           iteration : An integer value that respesents number of RFCOMM data trasfer iteration
+
+        Returns:
+            The throughput of the transfer in bits per second.
+        """
+        #An integer value designating the number of buffers to be sent.
+        num_of_buffers = 1
+        #An integer value designating the size of each buffer, in bytes.
+        buffer_size = 22000
+        throughput_list = []
+        for transfer in range(iteration):
+            (self.dut.droid.bluetoothConnectionThroughputSend(
+                num_of_buffers, buffer_size))
+
+            throughput = (
+                self.remote_device.droid.bluetoothConnectionThroughputRead(
+                    num_of_buffers, buffer_size))
+            throughput = throughput * 8
+            throughput_list.append(throughput)
+            self.log.info(
+                ("RFCOMM Throughput is :{} bits/sec".format(throughput)))
+        throughput = statistics.mean(throughput_list)
+        return throughput
+
+    def setup_hotspot_and_connect_client(self):
+        """
+        Setup hotspot on the remote device and client connects to hotspot
+
+        """
+        self.network = {
+            wutils.WifiEnums.SSID_KEY: 'Pixel_2G',
+            wutils.WifiEnums.PWD_KEY: '1234567890'
+        }
+        # Setup tethering on dut
+        wutils.start_wifi_tethering(self.android_devices[1],
+                                    self.network[wutils.WifiEnums.SSID_KEY],
+                                    self.network[wutils.WifiEnums.PWD_KEY],
+                                    WIFI_CONFIG_APBAND_2G)
+
+        # Connect client device to Hotspot
+        wutils.wifi_connect(self.dut, self.network, check_connectivity=False)
+
+    def setup_iperf_and_run_throughput(self):
+        self.iperf_server_address = self.android_devices[
+            1].droid.connectivityGetIPv4Addresses('wlan2')[0]
+        # Create the iperf config
+        iperf_config = {
+            'traffic_type': 'TCP',
+            'duration': self.audio_params['duration'] + self.IPERF_MARGIN,
+            'server_idx': 0,
+            'traffic_direction': 'UL',
+            'port': self.iperf_servers[0].port,
+            'start_meas_time': 4,
+        }
+        # Start iperf traffic (dut is the client)
+        self.client_iperf_helper = IperfHelper(iperf_config)
+        self.iperf_servers[0].start()
+        wputils.run_iperf_client_nonblocking(
+            self.dut, self.iperf_server_address,
+            self.client_iperf_helper.iperf_args)
+
+    def process_iperf_results(self):
+        time.sleep(self.IPERF_MARGIN + 2)
+        self.client_iperf_helper.process_iperf_results(self.dut, self.log,
+                                                       self.iperf_servers,
+                                                       self.test_name)
+        self.iperf_servers[0].stop()
+        return True
diff --git a/acts_tests/acts_contrib/test_utils/bt/ble_lib.py b/acts_tests/acts_contrib/test_utils/bt/ble_lib.py
index 4fac563..e399942 100644
--- a/acts_tests/acts_contrib/test_utils/bt/ble_lib.py
+++ b/acts_tests/acts_contrib/test_utils/bt/ble_lib.py
@@ -28,7 +28,6 @@
 from acts_contrib.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
 
 import time
-import os
 
 
 class BleLib():
@@ -42,9 +41,8 @@
 
     def _verify_ble_adv_started(self, advertise_callback):
         """Helper for verifying if an advertisment started or not"""
-        regex = "({}|{})".format(
-            adv_succ.format(advertise_callback),
-            adv_fail.format(advertise_callback))
+        regex = "({}|{})".format(adv_succ.format(advertise_callback),
+                                 adv_fail.format(advertise_callback))
         try:
             event = self.dut.ed.pop_events(regex, 5, small_timeout)
         except Empty:
@@ -72,8 +70,9 @@
                 advertise_callback, advertise_data, advertise_settings,
                 advertise_data)
         else:
-            self.dut.droid.bleStartBleAdvertising(
-                advertise_callback, advertise_data, advertise_settings)
+            self.dut.droid.bleStartBleAdvertising(advertise_callback,
+                                                  advertise_data,
+                                                  advertise_settings)
         if self._verify_ble_adv_started(advertise_callback):
             self.log.info(
                 "Tracking Callback ID: {}".format(advertise_callback))
@@ -150,8 +149,9 @@
         self.dut.droid.bleSetAdvertiseSettingsIsConnectable(False)
         advertise_callback, advertise_data, advertise_settings = (
             generate_ble_advertise_objects(self.dut.droid))
-        self.dut.droid.bleStartBleAdvertising(
-            advertise_callback, advertise_data, advertise_settings)
+        self.dut.droid.bleStartBleAdvertising(advertise_callback,
+                                              advertise_data,
+                                              advertise_settings)
         if self._verify_ble_adv_started(advertise_callback):
             self.log.info(
                 "Tracking Callback ID: {}".format(advertise_callback))
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 ce206da..4874fa7 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
@@ -15,7 +15,6 @@
 # the License.
 
 import time
-import os
 
 from acts.keys import Config
 from acts.utils import rand_ascii_str
@@ -182,7 +181,8 @@
             connected_devices = pri_dut.droid.bluetoothGetConnectedDevices()
             self.log.info(
                 "Waiting up to 10 seconds for device to reconnect...")
-            while time.time() < start_time + 10 and len(connected_devices) != 1:
+            while time.time() < start_time + 10 and len(
+                    connected_devices) != 1:
                 connected_devices = pri_dut.droid.bluetoothGetConnectedDevices(
                 )
                 time.sleep(1)
@@ -361,8 +361,8 @@
     def outgoing_call_multiple_iterations(self, pri_dut, sec_dut):
         iteration_count = 3
         self.log.info(
-            "Test outgoing call scenario from phone {} times from known contact".
-            format(iteration_count))
+            "Test outgoing call scenario from phone {} times from known contact"
+            .format(iteration_count))
         input("Press enter to execute this testcase...")
         outgoing_num = get_phone_number(self.log, sec_dut)
         for _ in range(iteration_count):
@@ -772,8 +772,9 @@
             carkit_response = volume_info_logcat[-1]['log_message'].split(',')
             for item in carkit_response:
                 if " volume=" in item:
-                    carkit_vol_response = int((
-                        int(item.split("=")[-1]) / android_volume_steps) * 100)
+                    carkit_vol_response = int(
+                        (int(item.split("=")[-1]) / android_volume_steps) *
+                        100)
                     self.log.info(
                         "Carkit set volume to {}%".format(carkit_vol_response))
         result = input(
diff --git a/acts_tests/acts_contrib/test_utils/bt/config_lib.py b/acts_tests/acts_contrib/test_utils/bt/config_lib.py
index 2926243..ca561fe 100644
--- a/acts_tests/acts_contrib/test_utils/bt/config_lib.py
+++ b/acts_tests/acts_contrib/test_utils/bt/config_lib.py
@@ -22,14 +22,13 @@
 from acts_contrib.test_utils.bt.bt_gatt_utils import setup_gatt_mtu
 from acts_contrib.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
 
-import time
 import os
 
 
 class ConfigLib():
     bluetooth_config_path = "/system/etc/bluetooth/bt_stack.conf"
-    conf_path = "{}/configs".format(
-        os.path.dirname(os.path.realpath(__file__)))
+    conf_path = "{}/configs".format(os.path.dirname(
+        os.path.realpath(__file__)))
     reset_config_path = "{}/bt_stack.conf".format(conf_path)
     non_bond_config_path = "{}/non_bond_bt_stack.conf".format(conf_path)
     disable_mitm_config_path = "{}/dis_mitm_bt_stack.conf".format(conf_path)
diff --git a/acts_tests/acts_contrib/test_utils/bt/gattc_lib.py b/acts_tests/acts_contrib/test_utils/bt/gattc_lib.py
index 86f0950..e79ee14 100644
--- a/acts_tests/acts_contrib/test_utils/bt/gattc_lib.py
+++ b/acts_tests/acts_contrib/test_utils/bt/gattc_lib.py
@@ -33,7 +33,6 @@
 from acts_contrib.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
 
 import time
-import os
 
 
 class GattClientLib():
diff --git a/acts_tests/acts_contrib/test_utils/bt/gatts_lib.py b/acts_tests/acts_contrib/test_utils/bt/gatts_lib.py
index ebfe1bd..0d274d1 100644
--- a/acts_tests/acts_contrib/test_utils/bt/gatts_lib.py
+++ b/acts_tests/acts_contrib/test_utils/bt/gatts_lib.py
@@ -15,7 +15,6 @@
 # the License.
 
 import time
-import os
 
 from acts.keys import Config
 from acts.utils import rand_ascii_str
@@ -145,8 +144,9 @@
                 else:
                     self.log.info("Execute result is false")
                 self.write_mapping = {}
-                self.dut.droid.gattServerSendResponse(
-                    self.gatt_server, 0, request_id, status, 0, [])
+                self.dut.droid.gattServerSendResponse(self.gatt_server, 0,
+                                                      request_id, status, 0,
+                                                      [])
                 continue
             offset = event['data']['offset']
             instance_id = event['data']['instanceId']
@@ -156,8 +156,8 @@
                         and event['data']['preparedWrite'] == True):
                     value = event['data']['value']
                     if instance_id in self.write_mapping.keys():
-                        self.write_mapping[
-                            instance_id] = self.write_mapping[instance_id] + value
+                        self.write_mapping[instance_id] = self.write_mapping[
+                            instance_id] + value
                         self.log.info(
                             "New Prepared Write Value for {}: {}".format(
                                 instance_id, self.write_mapping[instance_id]))
@@ -186,8 +186,9 @@
                 " [{}, {}, {}, {}]".
                 format(request_id, status, offset, data))
             data = data[offset:offset + mtu - 1]
-            self.dut.droid.gattServerSendResponse(
-                self.gatt_server, 0, request_id, status, offset, data)
+            self.dut.droid.gattServerSendResponse(self.gatt_server, 0,
+                                                  request_id, status, offset,
+                                                  data)
 
     def _setup_service(self, serv):
         service = self.dut.droid.gattServerCreateService(
@@ -304,8 +305,9 @@
                     "GATT Server Send Response [request_id, status, offset, " \
                     "data] [{}, {}, {}, {}]".format(request_id, status, offset,
                         data))
-                self.dut.droid.gattServerSendResponse(
-                    self.gatt_server, 0, request_id, status, offset, data)
+                self.dut.droid.gattServerSendResponse(self.gatt_server, 0,
+                                                      request_id, status,
+                                                      offset, data)
 
     def send_continuous_response_data(self, user_input):
         """Send the same response with data"""
@@ -356,7 +358,8 @@
                         value = event['data']['value']
                         if instance_id in self.write_mapping:
                             self.write_mapping[
-                                instance_id] = self.write_mapping[instance_id] + value
+                                instance_id] = self.write_mapping[
+                                    instance_id] + value
                         else:
                             self.write_mapping[instance_id] = value
                     else:
diff --git a/acts_tests/acts_contrib/test_utils/bt/loggers/bluetooth_metric_logger.py b/acts_tests/acts_contrib/test_utils/bt/loggers/bluetooth_metric_logger.py
index 98c925e..d82e433 100644
--- a/acts_tests/acts_contrib/test_utils/bt/loggers/bluetooth_metric_logger.py
+++ b/acts_tests/acts_contrib/test_utils/bt/loggers/bluetooth_metric_logger.py
@@ -16,7 +16,6 @@
 
 import base64
 from google.protobuf import message
-import os
 import time
 
 from acts.metrics.core import ProtoMetric
@@ -28,8 +27,8 @@
     """Assign values in dct to proto recursively."""
     for metric in dir(proto):
         if metric in dct:
-            if (isinstance(dct[metric], dict) and
-                    isinstance(getattr(proto, metric), message.Message)):
+            if (isinstance(dct[metric], dict)
+                    and isinstance(getattr(proto, metric), message.Message)):
                 recursive_assign(getattr(proto, metric), dct[metric])
             else:
                 setattr(proto, metric, dct[metric])
@@ -50,19 +49,20 @@
         self.results = []
         self.start_time = int(time.time())
 
-        self.proto_map = {'BluetoothPairAndConnectTest': self.proto_module
-                              .BluetoothPairAndConnectTestResult(),
-                          'BluetoothReconnectTest': self.proto_module
-                              .BluetoothReconnectTestResult(),
-                          'BluetoothThroughputTest': self.proto_module
-                              .BluetoothDataTestResult(),
-                          'BluetoothLatencyTest': self.proto_module
-                              .BluetoothDataTestResult(),
-                          'BtCodecSweepTest': self.proto_module
-                              .BluetoothAudioTestResult(),
-                          'BtRangeCodecTest': self.proto_module
-                              .BluetoothAudioTestResult(),
-                          }
+        self.proto_map = {
+            'BluetoothPairAndConnectTest':
+            self.proto_module.BluetoothPairAndConnectTestResult(),
+            'BluetoothReconnectTest':
+            self.proto_module.BluetoothReconnectTestResult(),
+            'BluetoothThroughputTest':
+            self.proto_module.BluetoothDataTestResult(),
+            'BluetoothLatencyTest':
+            self.proto_module.BluetoothDataTestResult(),
+            'BtCodecSweepTest':
+            self.proto_module.BluetoothAudioTestResult(),
+            'BtRangeCodecTest':
+            self.proto_module.BluetoothAudioTestResult(),
+        }
 
     @staticmethod
     def get_configuration_data(device):
@@ -82,21 +82,24 @@
 
         if device.__class__.__name__ == 'AndroidDevice':
             # TODO(b/124066126): Add remaining config data
-            data = {'device_class': 'phone',
-                    'device_model': device.model,
-                    'android_release_id': device.build_info['build_id'],
-                    'android_build_type': device.build_info['build_type'],
-                    'android_build_number': device.build_info[
-                        'incremental_build_id'],
-                    'android_branch_name': 'git_qt-release',
-                    'software_version': device.build_info['build_id']}
+            data = {
+                'device_class': 'phone',
+                'device_model': device.model,
+                'android_release_id': device.build_info['build_id'],
+                'android_build_type': device.build_info['build_type'],
+                'android_build_number':
+                device.build_info['incremental_build_id'],
+                'android_branch_name': 'git_qt-release',
+                'software_version': device.build_info['build_id']
+            }
 
         if device.__class__.__name__ == 'ParentDevice':
-            data = {'device_class': 'headset',
-                    'device_model': device.dut_type,
-                    'software_version': device.get_version()[1][
-                        'Fw Build Label'],
-                    'android_build_number': device.version}
+            data = {
+                'device_class': 'headset',
+                'device_model': device.dut_type,
+                'software_version': device.get_version()[1]['Fw Build Label'],
+                'android_build_number': device.version
+            }
 
         return data
 
@@ -128,10 +131,15 @@
 
     def get_proto_dict(self, test, proto):
         """Return dict with proto, readable ascii proto, and test name."""
-        return {'proto': base64.b64encode(ProtoMetric(test, proto)
-                                          .get_binary()).decode('utf-8'),
-                'proto_ascii': ProtoMetric(test, proto).get_ascii(),
-                'test_name': test}
+        return {
+            'proto':
+            base64.b64encode(ProtoMetric(test,
+                                         proto).get_binary()).decode('utf-8'),
+            'proto_ascii':
+            ProtoMetric(test, proto).get_ascii(),
+            'test_name':
+            test
+        }
 
     def add_proto_to_results(self, proto, test):
         """Adds proto as ProtoMetric object to self.results."""
diff --git a/acts_tests/acts_contrib/test_utils/bt/loggers/protos/bluetooth_metric_pb2.py b/acts_tests/acts_contrib/test_utils/bt/loggers/protos/bluetooth_metric_pb2.py
index d3c0a9f..bc7837c 100644
--- a/acts_tests/acts_contrib/test_utils/bt/loggers/protos/bluetooth_metric_pb2.py
+++ b/acts_tests/acts_contrib/test_utils/bt/loggers/protos/bluetooth_metric_pb2.py
@@ -2,9 +2,9 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: bluetooth_metric.proto
 """Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -13,819 +13,33 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='bluetooth_metric.proto',
-  package='wireless.android.platform.testing.bluetooth.metrics',
-  syntax='proto2',
-  serialized_options=None,
-  create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x16\x62luetooth_metric.proto\x12\x33wireless.android.platform.testing.bluetooth.metrics\"\xe8\x01\n\x13\x42luetoothTestDevice\x12\x14\n\x0c\x64\x65vice_class\x18\x01 \x01(\t\x12\x14\n\x0c\x64\x65vice_model\x18\x02 \x01(\t\x12\x18\n\x10hardware_version\x18\x03 \x01(\t\x12\x18\n\x10software_version\x18\x04 \x01(\t\x12\x1a\n\x12\x61ndroid_build_type\x18\x05 \x01(\t\x12\x1b\n\x13\x61ndroid_branch_name\x18\x06 \x01(\t\x12\x1c\n\x14\x61ndroid_build_number\x18\x07 \x01(\t\x12\x1a\n\x12\x61ndroid_release_id\x18\x08 \x01(\t\"\x83\x02\n#BluetoothContinuousTestResultHeader\x12\x16\n\x0etest_date_time\x18\x01 \x01(\x03\x12`\n\x0eprimary_device\x18\x02 \x01(\x0b\x32H.wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice\x12\x62\n\x10\x63onnected_device\x18\x03 \x01(\x0b\x32H.wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice\"\xe0\x03\n\x1c\x42luetoothReconnectTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12 \n\x18\x63onnection_attempt_count\x18\x02 \x01(\x05\x12#\n\x1b\x63onnection_successful_count\x18\x03 \x01(\x05\x12\x1f\n\x17\x63onnection_failed_count\x18\x04 \x01(\x05\x12\"\n\x1a\x63onnection_max_time_millis\x18\x05 \x01(\x05\x12\"\n\x1a\x63onnection_min_time_millis\x18\x06 \x01(\x05\x12\"\n\x1a\x63onnection_avg_time_millis\x18\x07 \x01(\x05\x12&\n\x1e\x61\x63l_connection_max_time_millis\x18\x08 \x01(\x05\x12&\n\x1e\x61\x63l_connection_min_time_millis\x18\t \x01(\x05\x12&\n\x1e\x61\x63l_connection_avg_time_millis\x18\n \x01(\x05\"\xc7\x03\n!BluetoothPairAndConnectTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12\x1a\n\x12pair_attempt_count\x18\x02 \x01(\x05\x12\x1d\n\x15pair_successful_count\x18\x03 \x01(\x05\x12\x19\n\x11pair_failed_count\x18\x04 \x01(\x05\x12\x1c\n\x14pair_max_time_millis\x18\x05 \x01(\x05\x12\x1c\n\x14pair_min_time_millis\x18\x06 \x01(\x05\x12\x1c\n\x14pair_avg_time_millis\x18\x07 \x01(\x05\x12(\n first_connection_max_time_millis\x18\x08 \x01(\x05\x12(\n first_connection_min_time_millis\x18\t \x01(\x05\x12(\n first_connection_avg_time_millis\x18\n \x01(\x05\"\x9d\x02\n\x18\x42luetoothA2dpCodecConfig\x12t\n\ncodec_type\x18\x01 \x01(\x0e\x32`.wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.BluetoothA2dpCodec\x12\x13\n\x0bsample_rate\x18\x02 \x01(\x05\x12\x17\n\x0f\x62its_per_sample\x18\x03 \x01(\x05\x12\x14\n\x0c\x63hannel_mode\x18\x04 \x01(\x05\"G\n\x12\x42luetoothA2dpCodec\x12\x07\n\x03SBC\x10\x00\x12\x07\n\x03\x41\x41\x43\x10\x01\x12\x08\n\x04\x41PTX\x10\x02\x12\x0b\n\x07\x41PTX_HD\x10\x03\x12\x08\n\x04LDAC\x10\x04\"\xdb\x01\n\x12\x41udioTestDataPoint\x12\x30\n(timestamp_since_beginning_of_test_millis\x18\x01 \x01(\x03\x12\'\n\x1f\x61udio_streaming_duration_millis\x18\x02 \x01(\x03\x12\x16\n\x0e\x61ttenuation_db\x18\x03 \x01(\x05\x12\x34\n,total_harmonic_distortion_plus_noise_percent\x18\x04 \x01(\x02\x12\x1c\n\x14\x61udio_glitches_count\x18\x05 \x01(\x05\"\xf6\x05\n\x18\x42luetoothAudioTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12q\n\raudio_profile\x18\x02 \x01(\x0e\x32Z.wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.AudioProfile\x12 \n\x18\x61udio_latency_min_millis\x18\x03 \x01(\x05\x12 \n\x18\x61udio_latency_max_millis\x18\x04 \x01(\x05\x12 \n\x18\x61udio_latency_avg_millis\x18\x05 \x01(\x05\x12\x1c\n\x14\x61udio_glitches_count\x18\x06 \x01(\x05\x12\"\n\x1a\x61udio_missed_packets_count\x18\x07 \x01(\x05\x12,\n$total_harmonic_distortion_plus_noise\x18\x08 \x01(\x02\x12\'\n\x1f\x61udio_streaming_duration_millis\x18\t \x01(\x03\x12h\n\x11\x61\x32\x64p_codec_config\x18\n \x01(\x0b\x32M.wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig\x12\\\n\x0b\x64\x61ta_points\x18\x0b \x03(\x0b\x32G.wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint\"*\n\x0c\x41udioProfile\x12\x08\n\x04\x41\x32\x44P\x10\x00\x12\x07\n\x03HFP\x10\x01\x12\x07\n\x03HAP\x10\x02\"\xd5\x04\n\x17\x42luetoothDataTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12\x81\x01\n\x16\x64\x61ta_transfer_protocol\x18\x02 \x01(\x0e\x32\x61.wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.DataTransferProtocol\x12\x1f\n\x17\x64\x61ta_latency_min_millis\x18\x03 \x01(\x05\x12\x1f\n\x17\x64\x61ta_latency_max_millis\x18\x04 \x01(\x05\x12\x1f\n\x17\x64\x61ta_latency_avg_millis\x18\x05 \x01(\x05\x12,\n$data_throughput_min_bytes_per_second\x18\x06 \x01(\x05\x12,\n$data_throughput_max_bytes_per_second\x18\x07 \x01(\x05\x12,\n$data_throughput_avg_bytes_per_second\x18\x08 \x01(\x05\x12\x18\n\x10\x64\x61ta_packet_size\x18\t \x01(\x05\"9\n\x14\x44\x61taTransferProtocol\x12\n\n\x06RFCOMM\x10\x00\x12\t\n\x05L2CAP\x10\x01\x12\n\n\x06LE_COC\x10\x02'
-)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x62luetooth_metric.proto\x12\x33wireless.android.platform.testing.bluetooth.metrics\"\xe8\x01\n\x13\x42luetoothTestDevice\x12\x14\n\x0c\x64\x65vice_class\x18\x01 \x01(\t\x12\x14\n\x0c\x64\x65vice_model\x18\x02 \x01(\t\x12\x18\n\x10hardware_version\x18\x03 \x01(\t\x12\x18\n\x10software_version\x18\x04 \x01(\t\x12\x1a\n\x12\x61ndroid_build_type\x18\x05 \x01(\t\x12\x1b\n\x13\x61ndroid_branch_name\x18\x06 \x01(\t\x12\x1c\n\x14\x61ndroid_build_number\x18\x07 \x01(\t\x12\x1a\n\x12\x61ndroid_release_id\x18\x08 \x01(\t\"\x83\x02\n#BluetoothContinuousTestResultHeader\x12\x16\n\x0etest_date_time\x18\x01 \x01(\x03\x12`\n\x0eprimary_device\x18\x02 \x01(\x0b\x32H.wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice\x12\x62\n\x10\x63onnected_device\x18\x03 \x01(\x0b\x32H.wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice\"\xe0\x03\n\x1c\x42luetoothReconnectTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12 \n\x18\x63onnection_attempt_count\x18\x02 \x01(\x05\x12#\n\x1b\x63onnection_successful_count\x18\x03 \x01(\x05\x12\x1f\n\x17\x63onnection_failed_count\x18\x04 \x01(\x05\x12\"\n\x1a\x63onnection_max_time_millis\x18\x05 \x01(\x05\x12\"\n\x1a\x63onnection_min_time_millis\x18\x06 \x01(\x05\x12\"\n\x1a\x63onnection_avg_time_millis\x18\x07 \x01(\x05\x12&\n\x1e\x61\x63l_connection_max_time_millis\x18\x08 \x01(\x05\x12&\n\x1e\x61\x63l_connection_min_time_millis\x18\t \x01(\x05\x12&\n\x1e\x61\x63l_connection_avg_time_millis\x18\n \x01(\x05\"\xc7\x03\n!BluetoothPairAndConnectTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12\x1a\n\x12pair_attempt_count\x18\x02 \x01(\x05\x12\x1d\n\x15pair_successful_count\x18\x03 \x01(\x05\x12\x19\n\x11pair_failed_count\x18\x04 \x01(\x05\x12\x1c\n\x14pair_max_time_millis\x18\x05 \x01(\x05\x12\x1c\n\x14pair_min_time_millis\x18\x06 \x01(\x05\x12\x1c\n\x14pair_avg_time_millis\x18\x07 \x01(\x05\x12(\n first_connection_max_time_millis\x18\x08 \x01(\x05\x12(\n first_connection_min_time_millis\x18\t \x01(\x05\x12(\n first_connection_avg_time_millis\x18\n \x01(\x05\"\x9d\x02\n\x18\x42luetoothA2dpCodecConfig\x12t\n\ncodec_type\x18\x01 \x01(\x0e\x32`.wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.BluetoothA2dpCodec\x12\x13\n\x0bsample_rate\x18\x02 \x01(\x05\x12\x17\n\x0f\x62its_per_sample\x18\x03 \x01(\x05\x12\x14\n\x0c\x63hannel_mode\x18\x04 \x01(\x05\"G\n\x12\x42luetoothA2dpCodec\x12\x07\n\x03SBC\x10\x00\x12\x07\n\x03\x41\x41\x43\x10\x01\x12\x08\n\x04\x41PTX\x10\x02\x12\x0b\n\x07\x41PTX_HD\x10\x03\x12\x08\n\x04LDAC\x10\x04\"\xdb\x01\n\x12\x41udioTestDataPoint\x12\x30\n(timestamp_since_beginning_of_test_millis\x18\x01 \x01(\x03\x12\'\n\x1f\x61udio_streaming_duration_millis\x18\x02 \x01(\x03\x12\x16\n\x0e\x61ttenuation_db\x18\x03 \x01(\x05\x12\x34\n,total_harmonic_distortion_plus_noise_percent\x18\x04 \x01(\x02\x12\x1c\n\x14\x61udio_glitches_count\x18\x05 \x01(\x05\"\xf6\x05\n\x18\x42luetoothAudioTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12q\n\raudio_profile\x18\x02 \x01(\x0e\x32Z.wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.AudioProfile\x12 \n\x18\x61udio_latency_min_millis\x18\x03 \x01(\x05\x12 \n\x18\x61udio_latency_max_millis\x18\x04 \x01(\x05\x12 \n\x18\x61udio_latency_avg_millis\x18\x05 \x01(\x05\x12\x1c\n\x14\x61udio_glitches_count\x18\x06 \x01(\x05\x12\"\n\x1a\x61udio_missed_packets_count\x18\x07 \x01(\x05\x12,\n$total_harmonic_distortion_plus_noise\x18\x08 \x01(\x02\x12\'\n\x1f\x61udio_streaming_duration_millis\x18\t \x01(\x03\x12h\n\x11\x61\x32\x64p_codec_config\x18\n \x01(\x0b\x32M.wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig\x12\\\n\x0b\x64\x61ta_points\x18\x0b \x03(\x0b\x32G.wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint\"*\n\x0c\x41udioProfile\x12\x08\n\x04\x41\x32\x44P\x10\x00\x12\x07\n\x03HFP\x10\x01\x12\x07\n\x03HAP\x10\x02\"\xd5\x04\n\x17\x42luetoothDataTestResult\x12t\n\x12\x63onfiguration_data\x18\x01 \x01(\x0b\x32X.wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader\x12\x81\x01\n\x16\x64\x61ta_transfer_protocol\x18\x02 \x01(\x0e\x32\x61.wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.DataTransferProtocol\x12\x1f\n\x17\x64\x61ta_latency_min_millis\x18\x03 \x01(\x05\x12\x1f\n\x17\x64\x61ta_latency_max_millis\x18\x04 \x01(\x05\x12\x1f\n\x17\x64\x61ta_latency_avg_millis\x18\x05 \x01(\x05\x12,\n$data_throughput_min_bytes_per_second\x18\x06 \x01(\x05\x12,\n$data_throughput_max_bytes_per_second\x18\x07 \x01(\x05\x12,\n$data_throughput_avg_bytes_per_second\x18\x08 \x01(\x05\x12\x18\n\x10\x64\x61ta_packet_size\x18\t \x01(\x05\"9\n\x14\x44\x61taTransferProtocol\x12\n\n\x06RFCOMM\x10\x00\x12\t\n\x05L2CAP\x10\x01\x12\n\n\x06LE_COC\x10\x02')
 
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'bluetooth_metric_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-
-_BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC = _descriptor.EnumDescriptor(
-  name='BluetoothA2dpCodec',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.BluetoothA2dpCodec',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='SBC', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='AAC', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='APTX', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='APTX_HD', index=3, number=3,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='LDAC', index=4, number=4,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=1732,
-  serialized_end=1803,
-)
-_sym_db.RegisterEnumDescriptor(_BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC)
-
-_BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE = _descriptor.EnumDescriptor(
-  name='AudioProfile',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.AudioProfile',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='A2DP', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='HFP', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='HAP', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=2744,
-  serialized_end=2786,
-)
-_sym_db.RegisterEnumDescriptor(_BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE)
-
-_BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL = _descriptor.EnumDescriptor(
-  name='DataTransferProtocol',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.DataTransferProtocol',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='RFCOMM', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='L2CAP', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='LE_COC', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=3329,
-  serialized_end=3386,
-)
-_sym_db.RegisterEnumDescriptor(_BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL)
-
-
-_BLUETOOTHTESTDEVICE = _descriptor.Descriptor(
-  name='BluetoothTestDevice',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='device_class', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.device_class', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='device_model', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.device_model', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='hardware_version', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.hardware_version', index=2,
-      number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='software_version', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.software_version', index=3,
-      number=4, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='android_build_type', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.android_build_type', index=4,
-      number=5, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='android_branch_name', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.android_branch_name', index=5,
-      number=6, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='android_build_number', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.android_build_number', index=6,
-      number=7, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='android_release_id', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice.android_release_id', index=7,
-      number=8, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=80,
-  serialized_end=312,
-)
-
-
-_BLUETOOTHCONTINUOUSTESTRESULTHEADER = _descriptor.Descriptor(
-  name='BluetoothContinuousTestResultHeader',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='test_date_time', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader.test_date_time', index=0,
-      number=1, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='primary_device', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader.primary_device', index=1,
-      number=2, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='connected_device', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader.connected_device', index=2,
-      number=3, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=315,
-  serialized_end=574,
-)
-
-
-_BLUETOOTHRECONNECTTESTRESULT = _descriptor.Descriptor(
-  name='BluetoothReconnectTestResult',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='configuration_data', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.configuration_data', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='connection_attempt_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_attempt_count', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='connection_successful_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_successful_count', index=2,
-      number=3, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='connection_failed_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_failed_count', index=3,
-      number=4, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='connection_max_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_max_time_millis', index=4,
-      number=5, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='connection_min_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_min_time_millis', index=5,
-      number=6, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='connection_avg_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.connection_avg_time_millis', index=6,
-      number=7, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='acl_connection_max_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.acl_connection_max_time_millis', index=7,
-      number=8, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='acl_connection_min_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.acl_connection_min_time_millis', index=8,
-      number=9, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='acl_connection_avg_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult.acl_connection_avg_time_millis', index=9,
-      number=10, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=577,
-  serialized_end=1057,
-)
-
-
-_BLUETOOTHPAIRANDCONNECTTESTRESULT = _descriptor.Descriptor(
-  name='BluetoothPairAndConnectTestResult',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='configuration_data', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.configuration_data', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='pair_attempt_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_attempt_count', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='pair_successful_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_successful_count', index=2,
-      number=3, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='pair_failed_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_failed_count', index=3,
-      number=4, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='pair_max_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_max_time_millis', index=4,
-      number=5, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='pair_min_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_min_time_millis', index=5,
-      number=6, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='pair_avg_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.pair_avg_time_millis', index=6,
-      number=7, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='first_connection_max_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.first_connection_max_time_millis', index=7,
-      number=8, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='first_connection_min_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.first_connection_min_time_millis', index=8,
-      number=9, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='first_connection_avg_time_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult.first_connection_avg_time_millis', index=9,
-      number=10, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1060,
-  serialized_end=1515,
-)
-
-
-_BLUETOOTHA2DPCODECCONFIG = _descriptor.Descriptor(
-  name='BluetoothA2dpCodecConfig',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='codec_type', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.codec_type', index=0,
-      number=1, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='sample_rate', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.sample_rate', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='bits_per_sample', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.bits_per_sample', index=2,
-      number=3, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='channel_mode', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig.channel_mode', index=3,
-      number=4, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1518,
-  serialized_end=1803,
-)
-
-
-_AUDIOTESTDATAPOINT = _descriptor.Descriptor(
-  name='AudioTestDataPoint',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='timestamp_since_beginning_of_test_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint.timestamp_since_beginning_of_test_millis', index=0,
-      number=1, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='audio_streaming_duration_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint.audio_streaming_duration_millis', index=1,
-      number=2, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='attenuation_db', full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint.attenuation_db', index=2,
-      number=3, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='total_harmonic_distortion_plus_noise_percent', full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint.total_harmonic_distortion_plus_noise_percent', index=3,
-      number=4, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='audio_glitches_count', full_name='wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint.audio_glitches_count', index=4,
-      number=5, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1806,
-  serialized_end=2025,
-)
-
-
-_BLUETOOTHAUDIOTESTRESULT = _descriptor.Descriptor(
-  name='BluetoothAudioTestResult',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='configuration_data', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.configuration_data', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='audio_profile', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_profile', index=1,
-      number=2, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='audio_latency_min_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_latency_min_millis', index=2,
-      number=3, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='audio_latency_max_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_latency_max_millis', index=3,
-      number=4, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='audio_latency_avg_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_latency_avg_millis', index=4,
-      number=5, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='audio_glitches_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_glitches_count', index=5,
-      number=6, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='audio_missed_packets_count', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_missed_packets_count', index=6,
-      number=7, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='total_harmonic_distortion_plus_noise', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.total_harmonic_distortion_plus_noise', index=7,
-      number=8, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='audio_streaming_duration_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.audio_streaming_duration_millis', index=8,
-      number=9, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='a2dp_codec_config', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.a2dp_codec_config', index=9,
-      number=10, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='data_points', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult.data_points', index=10,
-      number=11, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2028,
-  serialized_end=2786,
-)
-
-
-_BLUETOOTHDATATESTRESULT = _descriptor.Descriptor(
-  name='BluetoothDataTestResult',
-  full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='configuration_data', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.configuration_data', index=0,
-      number=1, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='data_transfer_protocol', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_transfer_protocol', index=1,
-      number=2, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='data_latency_min_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_latency_min_millis', index=2,
-      number=3, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='data_latency_max_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_latency_max_millis', index=3,
-      number=4, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='data_latency_avg_millis', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_latency_avg_millis', index=4,
-      number=5, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='data_throughput_min_bytes_per_second', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_throughput_min_bytes_per_second', index=5,
-      number=6, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='data_throughput_max_bytes_per_second', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_throughput_max_bytes_per_second', index=6,
-      number=7, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='data_throughput_avg_bytes_per_second', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_throughput_avg_bytes_per_second', index=7,
-      number=8, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='data_packet_size', full_name='wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult.data_packet_size', index=8,
-      number=9, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2789,
-  serialized_end=3386,
-)
-
-_BLUETOOTHCONTINUOUSTESTRESULTHEADER.fields_by_name['primary_device'].message_type = _BLUETOOTHTESTDEVICE
-_BLUETOOTHCONTINUOUSTESTRESULTHEADER.fields_by_name['connected_device'].message_type = _BLUETOOTHTESTDEVICE
-_BLUETOOTHRECONNECTTESTRESULT.fields_by_name['configuration_data'].message_type = _BLUETOOTHCONTINUOUSTESTRESULTHEADER
-_BLUETOOTHPAIRANDCONNECTTESTRESULT.fields_by_name['configuration_data'].message_type = _BLUETOOTHCONTINUOUSTESTRESULTHEADER
-_BLUETOOTHA2DPCODECCONFIG.fields_by_name['codec_type'].enum_type = _BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC
-_BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC.containing_type = _BLUETOOTHA2DPCODECCONFIG
-_BLUETOOTHAUDIOTESTRESULT.fields_by_name['configuration_data'].message_type = _BLUETOOTHCONTINUOUSTESTRESULTHEADER
-_BLUETOOTHAUDIOTESTRESULT.fields_by_name['audio_profile'].enum_type = _BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE
-_BLUETOOTHAUDIOTESTRESULT.fields_by_name['a2dp_codec_config'].message_type = _BLUETOOTHA2DPCODECCONFIG
-_BLUETOOTHAUDIOTESTRESULT.fields_by_name['data_points'].message_type = _AUDIOTESTDATAPOINT
-_BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE.containing_type = _BLUETOOTHAUDIOTESTRESULT
-_BLUETOOTHDATATESTRESULT.fields_by_name['configuration_data'].message_type = _BLUETOOTHCONTINUOUSTESTRESULTHEADER
-_BLUETOOTHDATATESTRESULT.fields_by_name['data_transfer_protocol'].enum_type = _BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL
-_BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL.containing_type = _BLUETOOTHDATATESTRESULT
-DESCRIPTOR.message_types_by_name['BluetoothTestDevice'] = _BLUETOOTHTESTDEVICE
-DESCRIPTOR.message_types_by_name['BluetoothContinuousTestResultHeader'] = _BLUETOOTHCONTINUOUSTESTRESULTHEADER
-DESCRIPTOR.message_types_by_name['BluetoothReconnectTestResult'] = _BLUETOOTHRECONNECTTESTRESULT
-DESCRIPTOR.message_types_by_name['BluetoothPairAndConnectTestResult'] = _BLUETOOTHPAIRANDCONNECTTESTRESULT
-DESCRIPTOR.message_types_by_name['BluetoothA2dpCodecConfig'] = _BLUETOOTHA2DPCODECCONFIG
-DESCRIPTOR.message_types_by_name['AudioTestDataPoint'] = _AUDIOTESTDATAPOINT
-DESCRIPTOR.message_types_by_name['BluetoothAudioTestResult'] = _BLUETOOTHAUDIOTESTRESULT
-DESCRIPTOR.message_types_by_name['BluetoothDataTestResult'] = _BLUETOOTHDATATESTRESULT
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-BluetoothTestDevice = _reflection.GeneratedProtocolMessageType('BluetoothTestDevice', (_message.Message,), {
-  'DESCRIPTOR' : _BLUETOOTHTESTDEVICE,
-  '__module__' : 'bluetooth_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothTestDevice)
-  })
-_sym_db.RegisterMessage(BluetoothTestDevice)
-
-BluetoothContinuousTestResultHeader = _reflection.GeneratedProtocolMessageType('BluetoothContinuousTestResultHeader', (_message.Message,), {
-  'DESCRIPTOR' : _BLUETOOTHCONTINUOUSTESTRESULTHEADER,
-  '__module__' : 'bluetooth_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothContinuousTestResultHeader)
-  })
-_sym_db.RegisterMessage(BluetoothContinuousTestResultHeader)
-
-BluetoothReconnectTestResult = _reflection.GeneratedProtocolMessageType('BluetoothReconnectTestResult', (_message.Message,), {
-  'DESCRIPTOR' : _BLUETOOTHRECONNECTTESTRESULT,
-  '__module__' : 'bluetooth_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothReconnectTestResult)
-  })
-_sym_db.RegisterMessage(BluetoothReconnectTestResult)
-
-BluetoothPairAndConnectTestResult = _reflection.GeneratedProtocolMessageType('BluetoothPairAndConnectTestResult', (_message.Message,), {
-  'DESCRIPTOR' : _BLUETOOTHPAIRANDCONNECTTESTRESULT,
-  '__module__' : 'bluetooth_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothPairAndConnectTestResult)
-  })
-_sym_db.RegisterMessage(BluetoothPairAndConnectTestResult)
-
-BluetoothA2dpCodecConfig = _reflection.GeneratedProtocolMessageType('BluetoothA2dpCodecConfig', (_message.Message,), {
-  'DESCRIPTOR' : _BLUETOOTHA2DPCODECCONFIG,
-  '__module__' : 'bluetooth_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothA2dpCodecConfig)
-  })
-_sym_db.RegisterMessage(BluetoothA2dpCodecConfig)
-
-AudioTestDataPoint = _reflection.GeneratedProtocolMessageType('AudioTestDataPoint', (_message.Message,), {
-  'DESCRIPTOR' : _AUDIOTESTDATAPOINT,
-  '__module__' : 'bluetooth_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.AudioTestDataPoint)
-  })
-_sym_db.RegisterMessage(AudioTestDataPoint)
-
-BluetoothAudioTestResult = _reflection.GeneratedProtocolMessageType('BluetoothAudioTestResult', (_message.Message,), {
-  'DESCRIPTOR' : _BLUETOOTHAUDIOTESTRESULT,
-  '__module__' : 'bluetooth_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothAudioTestResult)
-  })
-_sym_db.RegisterMessage(BluetoothAudioTestResult)
-
-BluetoothDataTestResult = _reflection.GeneratedProtocolMessageType('BluetoothDataTestResult', (_message.Message,), {
-  'DESCRIPTOR' : _BLUETOOTHDATATESTRESULT,
-  '__module__' : 'bluetooth_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.bluetooth.metrics.BluetoothDataTestResult)
-  })
-_sym_db.RegisterMessage(BluetoothDataTestResult)
-
-
+  DESCRIPTOR._options = None
+  _BLUETOOTHTESTDEVICE._serialized_start=80
+  _BLUETOOTHTESTDEVICE._serialized_end=312
+  _BLUETOOTHCONTINUOUSTESTRESULTHEADER._serialized_start=315
+  _BLUETOOTHCONTINUOUSTESTRESULTHEADER._serialized_end=574
+  _BLUETOOTHRECONNECTTESTRESULT._serialized_start=577
+  _BLUETOOTHRECONNECTTESTRESULT._serialized_end=1057
+  _BLUETOOTHPAIRANDCONNECTTESTRESULT._serialized_start=1060
+  _BLUETOOTHPAIRANDCONNECTTESTRESULT._serialized_end=1515
+  _BLUETOOTHA2DPCODECCONFIG._serialized_start=1518
+  _BLUETOOTHA2DPCODECCONFIG._serialized_end=1803
+  _BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC._serialized_start=1732
+  _BLUETOOTHA2DPCODECCONFIG_BLUETOOTHA2DPCODEC._serialized_end=1803
+  _AUDIOTESTDATAPOINT._serialized_start=1806
+  _AUDIOTESTDATAPOINT._serialized_end=2025
+  _BLUETOOTHAUDIOTESTRESULT._serialized_start=2028
+  _BLUETOOTHAUDIOTESTRESULT._serialized_end=2786
+  _BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE._serialized_start=2744
+  _BLUETOOTHAUDIOTESTRESULT_AUDIOPROFILE._serialized_end=2786
+  _BLUETOOTHDATATESTRESULT._serialized_start=2789
+  _BLUETOOTHDATATESTRESULT._serialized_end=3386
+  _BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL._serialized_start=3329
+  _BLUETOOTHDATATESTRESULT_DATATRANSFERPROTOCOL._serialized_end=3386
 # @@protoc_insertion_point(module_scope)
diff --git a/acts_tests/acts_contrib/test_utils/bt/protos/bluetooth_pb2.py b/acts_tests/acts_contrib/test_utils/bt/protos/bluetooth_pb2.py
index bce90e4..1188f77 100644
--- a/acts_tests/acts_contrib/test_utils/bt/protos/bluetooth_pb2.py
+++ b/acts_tests/acts_contrib/test_utils/bt/protos/bluetooth_pb2.py
@@ -2,10 +2,9 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: bluetooth.proto
 """Generated protocol buffer code."""
-from google.protobuf.internal import enum_type_wrapper
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -14,1127 +13,52 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='bluetooth.proto',
-  package='bluetooth.metrics.BluetoothMetricsProto',
-  syntax='proto2',
-  serialized_options=b'\n\025com.android.bluetoothB\025BluetoothMetricsProtoH\003',
-  create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x0f\x62luetooth.proto\x12\'bluetooth.metrics.BluetoothMetricsProto\"\x8a\x05\n\x0c\x42luetoothLog\x12J\n\x07session\x18\x01 \x03(\x0b\x32\x39.bluetooth.metrics.BluetoothMetricsProto.BluetoothSession\x12\x46\n\npair_event\x18\x02 \x03(\x0b\x32\x32.bluetooth.metrics.BluetoothMetricsProto.PairEvent\x12\x46\n\nwake_event\x18\x03 \x03(\x0b\x32\x32.bluetooth.metrics.BluetoothMetricsProto.WakeEvent\x12\x46\n\nscan_event\x18\x04 \x03(\x0b\x32\x32.bluetooth.metrics.BluetoothMetricsProto.ScanEvent\x12\x1a\n\x12num_bonded_devices\x18\x05 \x01(\x05\x12\x1d\n\x15num_bluetooth_session\x18\x06 \x01(\x03\x12\x16\n\x0enum_pair_event\x18\x07 \x01(\x03\x12\x16\n\x0enum_wake_event\x18\x08 \x01(\x03\x12\x16\n\x0enum_scan_event\x18\t \x01(\x03\x12\x61\n\x18profile_connection_stats\x18\n \x03(\x0b\x32?.bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats\x12p\n headset_profile_connection_stats\x18\x0b \x03(\x0b\x32\x46.bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats\"\xdf\x01\n\nDeviceInfo\x12\x14\n\x0c\x64\x65vice_class\x18\x01 \x01(\x05\x12S\n\x0b\x64\x65vice_type\x18\x02 \x01(\x0e\x32>.bluetooth.metrics.BluetoothMetricsProto.DeviceInfo.DeviceType\"f\n\nDeviceType\x12\x17\n\x13\x44\x45VICE_TYPE_UNKNOWN\x10\x00\x12\x15\n\x11\x44\x45VICE_TYPE_BREDR\x10\x01\x12\x12\n\x0e\x44\x45VICE_TYPE_LE\x10\x02\x12\x14\n\x10\x44\x45VICE_TYPE_DUMO\x10\x03\"\x8f\x06\n\x10\x42luetoothSession\x12\x1c\n\x14session_duration_sec\x18\x02 \x01(\x03\x12v\n\x1a\x63onnection_technology_type\x18\x03 \x01(\x0e\x32R.bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.ConnectionTechnologyType\x12\x1d\n\x11\x64isconnect_reason\x18\x04 \x01(\tB\x02\x18\x01\x12P\n\x13\x64\x65vice_connected_to\x18\x05 \x01(\x0b\x32\x33.bluetooth.metrics.BluetoothMetricsProto.DeviceInfo\x12N\n\x0erfcomm_session\x18\x06 \x01(\x0b\x32\x36.bluetooth.metrics.BluetoothMetricsProto.RFCommSession\x12J\n\x0c\x61\x32\x64p_session\x18\x07 \x01(\x0b\x32\x34.bluetooth.metrics.BluetoothMetricsProto.A2DPSession\x12n\n\x16\x64isconnect_reason_type\x18\x08 \x01(\x0e\x32N.bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.DisconnectReasonType\"\x8b\x01\n\x18\x43onnectionTechnologyType\x12&\n\"CONNECTION_TECHNOLOGY_TYPE_UNKNOWN\x10\x00\x12!\n\x1d\x43ONNECTION_TECHNOLOGY_TYPE_LE\x10\x01\x12$\n CONNECTION_TECHNOLOGY_TYPE_BREDR\x10\x02\"Z\n\x14\x44isconnectReasonType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x10\n\x0cMETRICS_DUMP\x10\x01\x12#\n\x1fNEXT_START_WITHOUT_END_PREVIOUS\x10\x02\"3\n\rRFCommSession\x12\x10\n\x08rx_bytes\x18\x01 \x01(\x05\x12\x10\n\x08tx_bytes\x18\x02 \x01(\x05\"\xf9\x02\n\x0b\x41\x32\x44PSession\x12\x1e\n\x16media_timer_min_millis\x18\x01 \x01(\x05\x12\x1e\n\x16media_timer_max_millis\x18\x02 \x01(\x05\x12\x1e\n\x16media_timer_avg_millis\x18\x03 \x01(\x05\x12!\n\x19\x62uffer_overruns_max_count\x18\x04 \x01(\x05\x12\x1d\n\x15\x62uffer_overruns_total\x18\x05 \x01(\x05\x12 \n\x18\x62uffer_underruns_average\x18\x06 \x01(\x02\x12\x1e\n\x16\x62uffer_underruns_count\x18\x07 \x01(\x05\x12\x1d\n\x15\x61udio_duration_millis\x18\x08 \x01(\x03\x12N\n\x0csource_codec\x18\t \x01(\x0e\x32\x38.bluetooth.metrics.BluetoothMetricsProto.A2dpSourceCodec\x12\x17\n\x0fis_a2dp_offload\x18\n \x01(\x08\"\x92\x01\n\tPairEvent\x12\x19\n\x11\x64isconnect_reason\x18\x01 \x01(\x05\x12\x19\n\x11\x65vent_time_millis\x18\x02 \x01(\x03\x12O\n\x12\x64\x65vice_paired_with\x18\x03 \x01(\x0b\x32\x33.bluetooth.metrics.BluetoothMetricsProto.DeviceInfo\"\xdc\x01\n\tWakeEvent\x12Y\n\x0fwake_event_type\x18\x01 \x01(\x0e\x32@.bluetooth.metrics.BluetoothMetricsProto.WakeEvent.WakeEventType\x12\x11\n\trequestor\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x19\n\x11\x65vent_time_millis\x18\x04 \x01(\x03\"8\n\rWakeEventType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0c\n\x08\x41\x43QUIRED\x10\x01\x12\x0c\n\x08RELEASED\x10\x02\"\xc4\x03\n\tScanEvent\x12Y\n\x0fscan_event_type\x18\x01 \x01(\x0e\x32@.bluetooth.metrics.BluetoothMetricsProto.ScanEvent.ScanEventType\x12\x11\n\tinitiator\x18\x02 \x01(\t\x12\x63\n\x14scan_technology_type\x18\x03 \x01(\x0e\x32\x45.bluetooth.metrics.BluetoothMetricsProto.ScanEvent.ScanTechnologyType\x12\x16\n\x0enumber_results\x18\x04 \x01(\x05\x12\x19\n\x11\x65vent_time_millis\x18\x05 \x01(\x03\"u\n\x12ScanTechnologyType\x12\x15\n\x11SCAN_TYPE_UNKNOWN\x10\x00\x12\x15\n\x11SCAN_TECH_TYPE_LE\x10\x01\x12\x18\n\x14SCAN_TECH_TYPE_BREDR\x10\x02\x12\x17\n\x13SCAN_TECH_TYPE_BOTH\x10\x03\":\n\rScanEventType\x12\x14\n\x10SCAN_EVENT_START\x10\x00\x12\x13\n\x0fSCAN_EVENT_STOP\x10\x01\"}\n\x16ProfileConnectionStats\x12\x46\n\nprofile_id\x18\x01 \x01(\x0e\x32\x32.bluetooth.metrics.BluetoothMetricsProto.ProfileId\x12\x1b\n\x13num_times_connected\x18\x02 \x01(\x05\"\x97\x01\n\x1dHeadsetProfileConnectionStats\x12Y\n\x14headset_profile_type\x18\x01 \x01(\x0e\x32;.bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileType\x12\x1b\n\x13num_times_connected\x18\x02 \x01(\x05*\xbd\x01\n\x0f\x41\x32\x64pSourceCodec\x12\x1d\n\x19\x41\x32\x44P_SOURCE_CODEC_UNKNOWN\x10\x00\x12\x19\n\x15\x41\x32\x44P_SOURCE_CODEC_SBC\x10\x01\x12\x19\n\x15\x41\x32\x44P_SOURCE_CODEC_AAC\x10\x02\x12\x1a\n\x16\x41\x32\x44P_SOURCE_CODEC_APTX\x10\x03\x12\x1d\n\x19\x41\x32\x44P_SOURCE_CODEC_APTX_HD\x10\x04\x12\x1a\n\x16\x41\x32\x44P_SOURCE_CODEC_LDAC\x10\x05*\xa0\x02\n\tProfileId\x12\x13\n\x0fPROFILE_UNKNOWN\x10\x00\x12\x0b\n\x07HEADSET\x10\x01\x12\x08\n\x04\x41\x32\x44P\x10\x02\x12\n\n\x06HEALTH\x10\x03\x12\x0c\n\x08HID_HOST\x10\x04\x12\x07\n\x03PAN\x10\x05\x12\x08\n\x04PBAP\x10\x06\x12\x08\n\x04GATT\x10\x07\x12\x0f\n\x0bGATT_SERVER\x10\x08\x12\x07\n\x03MAP\x10\t\x12\x07\n\x03SAP\x10\n\x12\r\n\tA2DP_SINK\x10\x0b\x12\x14\n\x10\x41VRCP_CONTROLLER\x10\x0c\x12\t\n\x05\x41VRCP\x10\r\x12\x12\n\x0eHEADSET_CLIENT\x10\x10\x12\x0f\n\x0bPBAP_CLIENT\x10\x11\x12\x0e\n\nMAP_CLIENT\x10\x12\x12\x0e\n\nHID_DEVICE\x10\x13\x12\x07\n\x03OPP\x10\x14\x12\x0f\n\x0bHEARING_AID\x10\x15*C\n\x12HeadsetProfileType\x12\x1b\n\x17HEADSET_PROFILE_UNKNOWN\x10\x00\x12\x07\n\x03HSP\x10\x01\x12\x07\n\x03HFP\x10\x02\x42\x30\n\x15\x63om.android.bluetoothB\x15\x42luetoothMetricsProtoH\x03'
-)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0f\x62luetooth.proto\x12\'bluetooth.metrics.BluetoothMetricsProto\"\x8a\x05\n\x0c\x42luetoothLog\x12J\n\x07session\x18\x01 \x03(\x0b\x32\x39.bluetooth.metrics.BluetoothMetricsProto.BluetoothSession\x12\x46\n\npair_event\x18\x02 \x03(\x0b\x32\x32.bluetooth.metrics.BluetoothMetricsProto.PairEvent\x12\x46\n\nwake_event\x18\x03 \x03(\x0b\x32\x32.bluetooth.metrics.BluetoothMetricsProto.WakeEvent\x12\x46\n\nscan_event\x18\x04 \x03(\x0b\x32\x32.bluetooth.metrics.BluetoothMetricsProto.ScanEvent\x12\x1a\n\x12num_bonded_devices\x18\x05 \x01(\x05\x12\x1d\n\x15num_bluetooth_session\x18\x06 \x01(\x03\x12\x16\n\x0enum_pair_event\x18\x07 \x01(\x03\x12\x16\n\x0enum_wake_event\x18\x08 \x01(\x03\x12\x16\n\x0enum_scan_event\x18\t \x01(\x03\x12\x61\n\x18profile_connection_stats\x18\n \x03(\x0b\x32?.bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats\x12p\n headset_profile_connection_stats\x18\x0b \x03(\x0b\x32\x46.bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats\"\xdf\x01\n\nDeviceInfo\x12\x14\n\x0c\x64\x65vice_class\x18\x01 \x01(\x05\x12S\n\x0b\x64\x65vice_type\x18\x02 \x01(\x0e\x32>.bluetooth.metrics.BluetoothMetricsProto.DeviceInfo.DeviceType\"f\n\nDeviceType\x12\x17\n\x13\x44\x45VICE_TYPE_UNKNOWN\x10\x00\x12\x15\n\x11\x44\x45VICE_TYPE_BREDR\x10\x01\x12\x12\n\x0e\x44\x45VICE_TYPE_LE\x10\x02\x12\x14\n\x10\x44\x45VICE_TYPE_DUMO\x10\x03\"\x8f\x06\n\x10\x42luetoothSession\x12\x1c\n\x14session_duration_sec\x18\x02 \x01(\x03\x12v\n\x1a\x63onnection_technology_type\x18\x03 \x01(\x0e\x32R.bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.ConnectionTechnologyType\x12\x1d\n\x11\x64isconnect_reason\x18\x04 \x01(\tB\x02\x18\x01\x12P\n\x13\x64\x65vice_connected_to\x18\x05 \x01(\x0b\x32\x33.bluetooth.metrics.BluetoothMetricsProto.DeviceInfo\x12N\n\x0erfcomm_session\x18\x06 \x01(\x0b\x32\x36.bluetooth.metrics.BluetoothMetricsProto.RFCommSession\x12J\n\x0c\x61\x32\x64p_session\x18\x07 \x01(\x0b\x32\x34.bluetooth.metrics.BluetoothMetricsProto.A2DPSession\x12n\n\x16\x64isconnect_reason_type\x18\x08 \x01(\x0e\x32N.bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.DisconnectReasonType\"\x8b\x01\n\x18\x43onnectionTechnologyType\x12&\n\"CONNECTION_TECHNOLOGY_TYPE_UNKNOWN\x10\x00\x12!\n\x1d\x43ONNECTION_TECHNOLOGY_TYPE_LE\x10\x01\x12$\n CONNECTION_TECHNOLOGY_TYPE_BREDR\x10\x02\"Z\n\x14\x44isconnectReasonType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x10\n\x0cMETRICS_DUMP\x10\x01\x12#\n\x1fNEXT_START_WITHOUT_END_PREVIOUS\x10\x02\"3\n\rRFCommSession\x12\x10\n\x08rx_bytes\x18\x01 \x01(\x05\x12\x10\n\x08tx_bytes\x18\x02 \x01(\x05\"\xf9\x02\n\x0b\x41\x32\x44PSession\x12\x1e\n\x16media_timer_min_millis\x18\x01 \x01(\x05\x12\x1e\n\x16media_timer_max_millis\x18\x02 \x01(\x05\x12\x1e\n\x16media_timer_avg_millis\x18\x03 \x01(\x05\x12!\n\x19\x62uffer_overruns_max_count\x18\x04 \x01(\x05\x12\x1d\n\x15\x62uffer_overruns_total\x18\x05 \x01(\x05\x12 \n\x18\x62uffer_underruns_average\x18\x06 \x01(\x02\x12\x1e\n\x16\x62uffer_underruns_count\x18\x07 \x01(\x05\x12\x1d\n\x15\x61udio_duration_millis\x18\x08 \x01(\x03\x12N\n\x0csource_codec\x18\t \x01(\x0e\x32\x38.bluetooth.metrics.BluetoothMetricsProto.A2dpSourceCodec\x12\x17\n\x0fis_a2dp_offload\x18\n \x01(\x08\"\x92\x01\n\tPairEvent\x12\x19\n\x11\x64isconnect_reason\x18\x01 \x01(\x05\x12\x19\n\x11\x65vent_time_millis\x18\x02 \x01(\x03\x12O\n\x12\x64\x65vice_paired_with\x18\x03 \x01(\x0b\x32\x33.bluetooth.metrics.BluetoothMetricsProto.DeviceInfo\"\xdc\x01\n\tWakeEvent\x12Y\n\x0fwake_event_type\x18\x01 \x01(\x0e\x32@.bluetooth.metrics.BluetoothMetricsProto.WakeEvent.WakeEventType\x12\x11\n\trequestor\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x19\n\x11\x65vent_time_millis\x18\x04 \x01(\x03\"8\n\rWakeEventType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0c\n\x08\x41\x43QUIRED\x10\x01\x12\x0c\n\x08RELEASED\x10\x02\"\xc4\x03\n\tScanEvent\x12Y\n\x0fscan_event_type\x18\x01 \x01(\x0e\x32@.bluetooth.metrics.BluetoothMetricsProto.ScanEvent.ScanEventType\x12\x11\n\tinitiator\x18\x02 \x01(\t\x12\x63\n\x14scan_technology_type\x18\x03 \x01(\x0e\x32\x45.bluetooth.metrics.BluetoothMetricsProto.ScanEvent.ScanTechnologyType\x12\x16\n\x0enumber_results\x18\x04 \x01(\x05\x12\x19\n\x11\x65vent_time_millis\x18\x05 \x01(\x03\"u\n\x12ScanTechnologyType\x12\x15\n\x11SCAN_TYPE_UNKNOWN\x10\x00\x12\x15\n\x11SCAN_TECH_TYPE_LE\x10\x01\x12\x18\n\x14SCAN_TECH_TYPE_BREDR\x10\x02\x12\x17\n\x13SCAN_TECH_TYPE_BOTH\x10\x03\":\n\rScanEventType\x12\x14\n\x10SCAN_EVENT_START\x10\x00\x12\x13\n\x0fSCAN_EVENT_STOP\x10\x01\"}\n\x16ProfileConnectionStats\x12\x46\n\nprofile_id\x18\x01 \x01(\x0e\x32\x32.bluetooth.metrics.BluetoothMetricsProto.ProfileId\x12\x1b\n\x13num_times_connected\x18\x02 \x01(\x05\"\x97\x01\n\x1dHeadsetProfileConnectionStats\x12Y\n\x14headset_profile_type\x18\x01 \x01(\x0e\x32;.bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileType\x12\x1b\n\x13num_times_connected\x18\x02 \x01(\x05*\xbd\x01\n\x0f\x41\x32\x64pSourceCodec\x12\x1d\n\x19\x41\x32\x44P_SOURCE_CODEC_UNKNOWN\x10\x00\x12\x19\n\x15\x41\x32\x44P_SOURCE_CODEC_SBC\x10\x01\x12\x19\n\x15\x41\x32\x44P_SOURCE_CODEC_AAC\x10\x02\x12\x1a\n\x16\x41\x32\x44P_SOURCE_CODEC_APTX\x10\x03\x12\x1d\n\x19\x41\x32\x44P_SOURCE_CODEC_APTX_HD\x10\x04\x12\x1a\n\x16\x41\x32\x44P_SOURCE_CODEC_LDAC\x10\x05*\xa0\x02\n\tProfileId\x12\x13\n\x0fPROFILE_UNKNOWN\x10\x00\x12\x0b\n\x07HEADSET\x10\x01\x12\x08\n\x04\x41\x32\x44P\x10\x02\x12\n\n\x06HEALTH\x10\x03\x12\x0c\n\x08HID_HOST\x10\x04\x12\x07\n\x03PAN\x10\x05\x12\x08\n\x04PBAP\x10\x06\x12\x08\n\x04GATT\x10\x07\x12\x0f\n\x0bGATT_SERVER\x10\x08\x12\x07\n\x03MAP\x10\t\x12\x07\n\x03SAP\x10\n\x12\r\n\tA2DP_SINK\x10\x0b\x12\x14\n\x10\x41VRCP_CONTROLLER\x10\x0c\x12\t\n\x05\x41VRCP\x10\r\x12\x12\n\x0eHEADSET_CLIENT\x10\x10\x12\x0f\n\x0bPBAP_CLIENT\x10\x11\x12\x0e\n\nMAP_CLIENT\x10\x12\x12\x0e\n\nHID_DEVICE\x10\x13\x12\x07\n\x03OPP\x10\x14\x12\x0f\n\x0bHEARING_AID\x10\x15*C\n\x12HeadsetProfileType\x12\x1b\n\x17HEADSET_PROFILE_UNKNOWN\x10\x00\x12\x07\n\x03HSP\x10\x01\x12\x07\n\x03HFP\x10\x02\x42\x30\n\x15\x63om.android.bluetoothB\x15\x42luetoothMetricsProtoH\x03')
 
-_A2DPSOURCECODEC = _descriptor.EnumDescriptor(
-  name='A2dpSourceCodec',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.A2dpSourceCodec',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='A2DP_SOURCE_CODEC_UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='A2DP_SOURCE_CODEC_SBC', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='A2DP_SOURCE_CODEC_AAC', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='A2DP_SOURCE_CODEC_APTX', index=3, number=3,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='A2DP_SOURCE_CODEC_APTX_HD', index=4, number=4,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='A2DP_SOURCE_CODEC_LDAC', index=5, number=5,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=3267,
-  serialized_end=3456,
-)
-_sym_db.RegisterEnumDescriptor(_A2DPSOURCECODEC)
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'bluetooth_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-A2dpSourceCodec = enum_type_wrapper.EnumTypeWrapper(_A2DPSOURCECODEC)
-_PROFILEID = _descriptor.EnumDescriptor(
-  name='ProfileId',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.ProfileId',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='PROFILE_UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='HEADSET', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='A2DP', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='HEALTH', index=3, number=3,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='HID_HOST', index=4, number=4,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='PAN', index=5, number=5,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='PBAP', index=6, number=6,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='GATT', index=7, number=7,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='GATT_SERVER', index=8, number=8,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='MAP', index=9, number=9,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='SAP', index=10, number=10,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='A2DP_SINK', index=11, number=11,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='AVRCP_CONTROLLER', index=12, number=12,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='AVRCP', index=13, number=13,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='HEADSET_CLIENT', index=14, number=16,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='PBAP_CLIENT', index=15, number=17,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='MAP_CLIENT', index=16, number=18,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='HID_DEVICE', index=17, number=19,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='OPP', index=18, number=20,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='HEARING_AID', index=19, number=21,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=3459,
-  serialized_end=3747,
-)
-_sym_db.RegisterEnumDescriptor(_PROFILEID)
-
-ProfileId = enum_type_wrapper.EnumTypeWrapper(_PROFILEID)
-_HEADSETPROFILETYPE = _descriptor.EnumDescriptor(
-  name='HeadsetProfileType',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileType',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='HEADSET_PROFILE_UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='HSP', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='HFP', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=3749,
-  serialized_end=3816,
-)
-_sym_db.RegisterEnumDescriptor(_HEADSETPROFILETYPE)
-
-HeadsetProfileType = enum_type_wrapper.EnumTypeWrapper(_HEADSETPROFILETYPE)
-A2DP_SOURCE_CODEC_UNKNOWN = 0
-A2DP_SOURCE_CODEC_SBC = 1
-A2DP_SOURCE_CODEC_AAC = 2
-A2DP_SOURCE_CODEC_APTX = 3
-A2DP_SOURCE_CODEC_APTX_HD = 4
-A2DP_SOURCE_CODEC_LDAC = 5
-PROFILE_UNKNOWN = 0
-HEADSET = 1
-A2DP = 2
-HEALTH = 3
-HID_HOST = 4
-PAN = 5
-PBAP = 6
-GATT = 7
-GATT_SERVER = 8
-MAP = 9
-SAP = 10
-A2DP_SINK = 11
-AVRCP_CONTROLLER = 12
-AVRCP = 13
-HEADSET_CLIENT = 16
-PBAP_CLIENT = 17
-MAP_CLIENT = 18
-HID_DEVICE = 19
-OPP = 20
-HEARING_AID = 21
-HEADSET_PROFILE_UNKNOWN = 0
-HSP = 1
-HFP = 2
-
-
-_DEVICEINFO_DEVICETYPE = _descriptor.EnumDescriptor(
-  name='DeviceType',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.DeviceInfo.DeviceType',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='DEVICE_TYPE_UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='DEVICE_TYPE_BREDR', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='DEVICE_TYPE_LE', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='DEVICE_TYPE_DUMO', index=3, number=3,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=835,
-  serialized_end=937,
-)
-_sym_db.RegisterEnumDescriptor(_DEVICEINFO_DEVICETYPE)
-
-_BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE = _descriptor.EnumDescriptor(
-  name='ConnectionTechnologyType',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.ConnectionTechnologyType',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='CONNECTION_TECHNOLOGY_TYPE_UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='CONNECTION_TECHNOLOGY_TYPE_LE', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='CONNECTION_TECHNOLOGY_TYPE_BREDR', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=1492,
-  serialized_end=1631,
-)
-_sym_db.RegisterEnumDescriptor(_BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE)
-
-_BLUETOOTHSESSION_DISCONNECTREASONTYPE = _descriptor.EnumDescriptor(
-  name='DisconnectReasonType',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.DisconnectReasonType',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='METRICS_DUMP', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='NEXT_START_WITHOUT_END_PREVIOUS', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=1633,
-  serialized_end=1723,
-)
-_sym_db.RegisterEnumDescriptor(_BLUETOOTHSESSION_DISCONNECTREASONTYPE)
-
-_WAKEEVENT_WAKEEVENTTYPE = _descriptor.EnumDescriptor(
-  name='WakeEventType',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent.WakeEventType',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='ACQUIRED', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='RELEASED', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=2472,
-  serialized_end=2528,
-)
-_sym_db.RegisterEnumDescriptor(_WAKEEVENT_WAKEEVENTTYPE)
-
-_SCANEVENT_SCANTECHNOLOGYTYPE = _descriptor.EnumDescriptor(
-  name='ScanTechnologyType',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.ScanTechnologyType',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='SCAN_TYPE_UNKNOWN', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='SCAN_TECH_TYPE_LE', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='SCAN_TECH_TYPE_BREDR', index=2, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='SCAN_TECH_TYPE_BOTH', index=3, number=3,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=2806,
-  serialized_end=2923,
-)
-_sym_db.RegisterEnumDescriptor(_SCANEVENT_SCANTECHNOLOGYTYPE)
-
-_SCANEVENT_SCANEVENTTYPE = _descriptor.EnumDescriptor(
-  name='ScanEventType',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.ScanEventType',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='SCAN_EVENT_START', index=0, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='SCAN_EVENT_STOP', index=1, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=2925,
-  serialized_end=2983,
-)
-_sym_db.RegisterEnumDescriptor(_SCANEVENT_SCANEVENTTYPE)
-
-
-_BLUETOOTHLOG = _descriptor.Descriptor(
-  name='BluetoothLog',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='session', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.session', index=0,
-      number=1, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='pair_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.pair_event', index=1,
-      number=2, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='wake_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.wake_event', index=2,
-      number=3, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='scan_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.scan_event', index=3,
-      number=4, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='num_bonded_devices', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.num_bonded_devices', index=4,
-      number=5, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='num_bluetooth_session', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.num_bluetooth_session', index=5,
-      number=6, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='num_pair_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.num_pair_event', index=6,
-      number=7, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='num_wake_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.num_wake_event', index=7,
-      number=8, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='num_scan_event', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.num_scan_event', index=8,
-      number=9, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='profile_connection_stats', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.profile_connection_stats', index=9,
-      number=10, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='headset_profile_connection_stats', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothLog.headset_profile_connection_stats', index=10,
-      number=11, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=61,
-  serialized_end=711,
-)
-
-
-_DEVICEINFO = _descriptor.Descriptor(
-  name='DeviceInfo',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.DeviceInfo',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='device_class', full_name='bluetooth.metrics.BluetoothMetricsProto.DeviceInfo.device_class', index=0,
-      number=1, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='device_type', full_name='bluetooth.metrics.BluetoothMetricsProto.DeviceInfo.device_type', index=1,
-      number=2, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _DEVICEINFO_DEVICETYPE,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=714,
-  serialized_end=937,
-)
-
-
-_BLUETOOTHSESSION = _descriptor.Descriptor(
-  name='BluetoothSession',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='session_duration_sec', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.session_duration_sec', index=0,
-      number=2, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='connection_technology_type', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.connection_technology_type', index=1,
-      number=3, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='disconnect_reason', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.disconnect_reason', index=2,
-      number=4, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=b'\030\001', file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='device_connected_to', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.device_connected_to', index=3,
-      number=5, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='rfcomm_session', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.rfcomm_session', index=4,
-      number=6, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='a2dp_session', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.a2dp_session', index=5,
-      number=7, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='disconnect_reason_type', full_name='bluetooth.metrics.BluetoothMetricsProto.BluetoothSession.disconnect_reason_type', index=6,
-      number=8, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE,
-    _BLUETOOTHSESSION_DISCONNECTREASONTYPE,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=940,
-  serialized_end=1723,
-)
-
-
-_RFCOMMSESSION = _descriptor.Descriptor(
-  name='RFCommSession',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.RFCommSession',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='rx_bytes', full_name='bluetooth.metrics.BluetoothMetricsProto.RFCommSession.rx_bytes', index=0,
-      number=1, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='tx_bytes', full_name='bluetooth.metrics.BluetoothMetricsProto.RFCommSession.tx_bytes', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1725,
-  serialized_end=1776,
-)
-
-
-_A2DPSESSION = _descriptor.Descriptor(
-  name='A2DPSession',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='media_timer_min_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.media_timer_min_millis', index=0,
-      number=1, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='media_timer_max_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.media_timer_max_millis', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='media_timer_avg_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.media_timer_avg_millis', index=2,
-      number=3, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='buffer_overruns_max_count', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.buffer_overruns_max_count', index=3,
-      number=4, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='buffer_overruns_total', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.buffer_overruns_total', index=4,
-      number=5, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='buffer_underruns_average', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.buffer_underruns_average', index=5,
-      number=6, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='buffer_underruns_count', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.buffer_underruns_count', index=6,
-      number=7, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='audio_duration_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.audio_duration_millis', index=7,
-      number=8, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='source_codec', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.source_codec', index=8,
-      number=9, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='is_a2dp_offload', full_name='bluetooth.metrics.BluetoothMetricsProto.A2DPSession.is_a2dp_offload', index=9,
-      number=10, type=8, cpp_type=7, label=1,
-      has_default_value=False, default_value=False,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=1779,
-  serialized_end=2156,
-)
-
-
-_PAIREVENT = _descriptor.Descriptor(
-  name='PairEvent',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.PairEvent',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='disconnect_reason', full_name='bluetooth.metrics.BluetoothMetricsProto.PairEvent.disconnect_reason', index=0,
-      number=1, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='event_time_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.PairEvent.event_time_millis', index=1,
-      number=2, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='device_paired_with', full_name='bluetooth.metrics.BluetoothMetricsProto.PairEvent.device_paired_with', index=2,
-      number=3, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2159,
-  serialized_end=2305,
-)
-
-
-_WAKEEVENT = _descriptor.Descriptor(
-  name='WakeEvent',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='wake_event_type', full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent.wake_event_type', index=0,
-      number=1, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='requestor', full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent.requestor', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='name', full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent.name', index=2,
-      number=3, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='event_time_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.WakeEvent.event_time_millis', index=3,
-      number=4, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _WAKEEVENT_WAKEEVENTTYPE,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2308,
-  serialized_end=2528,
-)
-
-
-_SCANEVENT = _descriptor.Descriptor(
-  name='ScanEvent',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='scan_event_type', full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.scan_event_type', index=0,
-      number=1, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='initiator', full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.initiator', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='scan_technology_type', full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.scan_technology_type', index=2,
-      number=3, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='number_results', full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.number_results', index=3,
-      number=4, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='event_time_millis', full_name='bluetooth.metrics.BluetoothMetricsProto.ScanEvent.event_time_millis', index=4,
-      number=5, type=3, cpp_type=2, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _SCANEVENT_SCANTECHNOLOGYTYPE,
-    _SCANEVENT_SCANEVENTTYPE,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2531,
-  serialized_end=2983,
-)
-
-
-_PROFILECONNECTIONSTATS = _descriptor.Descriptor(
-  name='ProfileConnectionStats',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='profile_id', full_name='bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats.profile_id', index=0,
-      number=1, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='num_times_connected', full_name='bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats.num_times_connected', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=2985,
-  serialized_end=3110,
-)
-
-
-_HEADSETPROFILECONNECTIONSTATS = _descriptor.Descriptor(
-  name='HeadsetProfileConnectionStats',
-  full_name='bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='headset_profile_type', full_name='bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats.headset_profile_type', index=0,
-      number=1, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='num_times_connected', full_name='bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats.num_times_connected', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=3113,
-  serialized_end=3264,
-)
-
-_BLUETOOTHLOG.fields_by_name['session'].message_type = _BLUETOOTHSESSION
-_BLUETOOTHLOG.fields_by_name['pair_event'].message_type = _PAIREVENT
-_BLUETOOTHLOG.fields_by_name['wake_event'].message_type = _WAKEEVENT
-_BLUETOOTHLOG.fields_by_name['scan_event'].message_type = _SCANEVENT
-_BLUETOOTHLOG.fields_by_name['profile_connection_stats'].message_type = _PROFILECONNECTIONSTATS
-_BLUETOOTHLOG.fields_by_name['headset_profile_connection_stats'].message_type = _HEADSETPROFILECONNECTIONSTATS
-_DEVICEINFO.fields_by_name['device_type'].enum_type = _DEVICEINFO_DEVICETYPE
-_DEVICEINFO_DEVICETYPE.containing_type = _DEVICEINFO
-_BLUETOOTHSESSION.fields_by_name['connection_technology_type'].enum_type = _BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE
-_BLUETOOTHSESSION.fields_by_name['device_connected_to'].message_type = _DEVICEINFO
-_BLUETOOTHSESSION.fields_by_name['rfcomm_session'].message_type = _RFCOMMSESSION
-_BLUETOOTHSESSION.fields_by_name['a2dp_session'].message_type = _A2DPSESSION
-_BLUETOOTHSESSION.fields_by_name['disconnect_reason_type'].enum_type = _BLUETOOTHSESSION_DISCONNECTREASONTYPE
-_BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE.containing_type = _BLUETOOTHSESSION
-_BLUETOOTHSESSION_DISCONNECTREASONTYPE.containing_type = _BLUETOOTHSESSION
-_A2DPSESSION.fields_by_name['source_codec'].enum_type = _A2DPSOURCECODEC
-_PAIREVENT.fields_by_name['device_paired_with'].message_type = _DEVICEINFO
-_WAKEEVENT.fields_by_name['wake_event_type'].enum_type = _WAKEEVENT_WAKEEVENTTYPE
-_WAKEEVENT_WAKEEVENTTYPE.containing_type = _WAKEEVENT
-_SCANEVENT.fields_by_name['scan_event_type'].enum_type = _SCANEVENT_SCANEVENTTYPE
-_SCANEVENT.fields_by_name['scan_technology_type'].enum_type = _SCANEVENT_SCANTECHNOLOGYTYPE
-_SCANEVENT_SCANTECHNOLOGYTYPE.containing_type = _SCANEVENT
-_SCANEVENT_SCANEVENTTYPE.containing_type = _SCANEVENT
-_PROFILECONNECTIONSTATS.fields_by_name['profile_id'].enum_type = _PROFILEID
-_HEADSETPROFILECONNECTIONSTATS.fields_by_name['headset_profile_type'].enum_type = _HEADSETPROFILETYPE
-DESCRIPTOR.message_types_by_name['BluetoothLog'] = _BLUETOOTHLOG
-DESCRIPTOR.message_types_by_name['DeviceInfo'] = _DEVICEINFO
-DESCRIPTOR.message_types_by_name['BluetoothSession'] = _BLUETOOTHSESSION
-DESCRIPTOR.message_types_by_name['RFCommSession'] = _RFCOMMSESSION
-DESCRIPTOR.message_types_by_name['A2DPSession'] = _A2DPSESSION
-DESCRIPTOR.message_types_by_name['PairEvent'] = _PAIREVENT
-DESCRIPTOR.message_types_by_name['WakeEvent'] = _WAKEEVENT
-DESCRIPTOR.message_types_by_name['ScanEvent'] = _SCANEVENT
-DESCRIPTOR.message_types_by_name['ProfileConnectionStats'] = _PROFILECONNECTIONSTATS
-DESCRIPTOR.message_types_by_name['HeadsetProfileConnectionStats'] = _HEADSETPROFILECONNECTIONSTATS
-DESCRIPTOR.enum_types_by_name['A2dpSourceCodec'] = _A2DPSOURCECODEC
-DESCRIPTOR.enum_types_by_name['ProfileId'] = _PROFILEID
-DESCRIPTOR.enum_types_by_name['HeadsetProfileType'] = _HEADSETPROFILETYPE
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-BluetoothLog = _reflection.GeneratedProtocolMessageType('BluetoothLog', (_message.Message,), {
-  'DESCRIPTOR' : _BLUETOOTHLOG,
-  '__module__' : 'bluetooth_pb2'
-  # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.BluetoothLog)
-  })
-_sym_db.RegisterMessage(BluetoothLog)
-
-DeviceInfo = _reflection.GeneratedProtocolMessageType('DeviceInfo', (_message.Message,), {
-  'DESCRIPTOR' : _DEVICEINFO,
-  '__module__' : 'bluetooth_pb2'
-  # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.DeviceInfo)
-  })
-_sym_db.RegisterMessage(DeviceInfo)
-
-BluetoothSession = _reflection.GeneratedProtocolMessageType('BluetoothSession', (_message.Message,), {
-  'DESCRIPTOR' : _BLUETOOTHSESSION,
-  '__module__' : 'bluetooth_pb2'
-  # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.BluetoothSession)
-  })
-_sym_db.RegisterMessage(BluetoothSession)
-
-RFCommSession = _reflection.GeneratedProtocolMessageType('RFCommSession', (_message.Message,), {
-  'DESCRIPTOR' : _RFCOMMSESSION,
-  '__module__' : 'bluetooth_pb2'
-  # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.RFCommSession)
-  })
-_sym_db.RegisterMessage(RFCommSession)
-
-A2DPSession = _reflection.GeneratedProtocolMessageType('A2DPSession', (_message.Message,), {
-  'DESCRIPTOR' : _A2DPSESSION,
-  '__module__' : 'bluetooth_pb2'
-  # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.A2DPSession)
-  })
-_sym_db.RegisterMessage(A2DPSession)
-
-PairEvent = _reflection.GeneratedProtocolMessageType('PairEvent', (_message.Message,), {
-  'DESCRIPTOR' : _PAIREVENT,
-  '__module__' : 'bluetooth_pb2'
-  # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.PairEvent)
-  })
-_sym_db.RegisterMessage(PairEvent)
-
-WakeEvent = _reflection.GeneratedProtocolMessageType('WakeEvent', (_message.Message,), {
-  'DESCRIPTOR' : _WAKEEVENT,
-  '__module__' : 'bluetooth_pb2'
-  # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.WakeEvent)
-  })
-_sym_db.RegisterMessage(WakeEvent)
-
-ScanEvent = _reflection.GeneratedProtocolMessageType('ScanEvent', (_message.Message,), {
-  'DESCRIPTOR' : _SCANEVENT,
-  '__module__' : 'bluetooth_pb2'
-  # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.ScanEvent)
-  })
-_sym_db.RegisterMessage(ScanEvent)
-
-ProfileConnectionStats = _reflection.GeneratedProtocolMessageType('ProfileConnectionStats', (_message.Message,), {
-  'DESCRIPTOR' : _PROFILECONNECTIONSTATS,
-  '__module__' : 'bluetooth_pb2'
-  # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.ProfileConnectionStats)
-  })
-_sym_db.RegisterMessage(ProfileConnectionStats)
-
-HeadsetProfileConnectionStats = _reflection.GeneratedProtocolMessageType('HeadsetProfileConnectionStats', (_message.Message,), {
-  'DESCRIPTOR' : _HEADSETPROFILECONNECTIONSTATS,
-  '__module__' : 'bluetooth_pb2'
-  # @@protoc_insertion_point(class_scope:bluetooth.metrics.BluetoothMetricsProto.HeadsetProfileConnectionStats)
-  })
-_sym_db.RegisterMessage(HeadsetProfileConnectionStats)
-
-
-DESCRIPTOR._options = None
-_BLUETOOTHSESSION.fields_by_name['disconnect_reason']._options = None
+  DESCRIPTOR._options = None
+  DESCRIPTOR._serialized_options = b'\n\025com.android.bluetoothB\025BluetoothMetricsProtoH\003'
+  _BLUETOOTHSESSION.fields_by_name['disconnect_reason']._options = None
+  _BLUETOOTHSESSION.fields_by_name['disconnect_reason']._serialized_options = b'\030\001'
+  _A2DPSOURCECODEC._serialized_start=3267
+  _A2DPSOURCECODEC._serialized_end=3456
+  _PROFILEID._serialized_start=3459
+  _PROFILEID._serialized_end=3747
+  _HEADSETPROFILETYPE._serialized_start=3749
+  _HEADSETPROFILETYPE._serialized_end=3816
+  _BLUETOOTHLOG._serialized_start=61
+  _BLUETOOTHLOG._serialized_end=711
+  _DEVICEINFO._serialized_start=714
+  _DEVICEINFO._serialized_end=937
+  _DEVICEINFO_DEVICETYPE._serialized_start=835
+  _DEVICEINFO_DEVICETYPE._serialized_end=937
+  _BLUETOOTHSESSION._serialized_start=940
+  _BLUETOOTHSESSION._serialized_end=1723
+  _BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE._serialized_start=1492
+  _BLUETOOTHSESSION_CONNECTIONTECHNOLOGYTYPE._serialized_end=1631
+  _BLUETOOTHSESSION_DISCONNECTREASONTYPE._serialized_start=1633
+  _BLUETOOTHSESSION_DISCONNECTREASONTYPE._serialized_end=1723
+  _RFCOMMSESSION._serialized_start=1725
+  _RFCOMMSESSION._serialized_end=1776
+  _A2DPSESSION._serialized_start=1779
+  _A2DPSESSION._serialized_end=2156
+  _PAIREVENT._serialized_start=2159
+  _PAIREVENT._serialized_end=2305
+  _WAKEEVENT._serialized_start=2308
+  _WAKEEVENT._serialized_end=2528
+  _WAKEEVENT_WAKEEVENTTYPE._serialized_start=2472
+  _WAKEEVENT_WAKEEVENTTYPE._serialized_end=2528
+  _SCANEVENT._serialized_start=2531
+  _SCANEVENT._serialized_end=2983
+  _SCANEVENT_SCANTECHNOLOGYTYPE._serialized_start=2806
+  _SCANEVENT_SCANTECHNOLOGYTYPE._serialized_end=2923
+  _SCANEVENT_SCANEVENTTYPE._serialized_start=2925
+  _SCANEVENT_SCANEVENTTYPE._serialized_end=2983
+  _PROFILECONNECTIONSTATS._serialized_start=2985
+  _PROFILECONNECTIONSTATS._serialized_end=3110
+  _HEADSETPROFILECONNECTIONSTATS._serialized_start=3113
+  _HEADSETPROFILECONNECTIONSTATS._serialized_end=3264
 # @@protoc_insertion_point(module_scope)
diff --git a/acts_tests/acts_contrib/test_utils/bt/pts/pts_base_class.py b/acts_tests/acts_contrib/test_utils/bt/pts/pts_base_class.py
index 6be3d2e..d96ce8a 100644
--- a/acts_tests/acts_contrib/test_utils/bt/pts/pts_base_class.py
+++ b/acts_tests/acts_contrib/test_utils/bt/pts/pts_base_class.py
@@ -17,14 +17,11 @@
 Tests.
 """
 
-import ctypes
-import random
 import re
 import time
 import traceback
 
 from ctypes import *
-from datetime import datetime
 
 from acts import signals
 from acts.base_test import BaseTestClass
diff --git a/acts_tests/acts_contrib/test_utils/bt/rfcomm_lib.py b/acts_tests/acts_contrib/test_utils/bt/rfcomm_lib.py
index f9aa117..43c754b 100644
--- a/acts_tests/acts_contrib/test_utils/bt/rfcomm_lib.py
+++ b/acts_tests/acts_contrib/test_utils/bt/rfcomm_lib.py
@@ -20,8 +20,6 @@
 from acts_contrib.test_utils.bt.bt_constants import bt_rfcomm_uuids
 from acts_contrib.test_utils.bt.bt_test_utils import set_bt_scan_mode
 
-import pprint
-
 
 class RfcommLib():
     def __init__(self, log, dut, target_mac_addr=None):
diff --git a/acts_tests/acts_contrib/test_utils/car/car_bt_utils.py b/acts_tests/acts_contrib/test_utils/car/car_bt_utils.py
index a428528..b64be22 100644
--- a/acts_tests/acts_contrib/test_utils/car/car_bt_utils.py
+++ b/acts_tests/acts_contrib/test_utils/car/car_bt_utils.py
@@ -26,9 +26,6 @@
 # NOTE: This util is applicable to only non-conference type calls. It is best
 # suited to test cases where only one call is in action at any point of time.
 
-import queue
-import time
-
 from acts import logger
 from acts_contrib.test_utils.bt import bt_test_utils
 from acts_contrib.test_utils.bt.BtEnum import *
@@ -48,8 +45,9 @@
         True if success, False if fail.
     """
     # TODO investigate MCE
-    car_profiles = [BluetoothProfile.A2DP_SINK,
-                    BluetoothProfile.HEADSET_CLIENT,
-                    BluetoothProfile.PBAP_CLIENT, BluetoothProfile.MAP_MCE]
+    car_profiles = [
+        BluetoothProfile.A2DP_SINK, BluetoothProfile.HEADSET_CLIENT,
+        BluetoothProfile.PBAP_CLIENT, BluetoothProfile.MAP_MCE
+    ]
     bt_test_utils.set_profile_priority(car_droid, ph_droid, car_profiles,
                                        BluetoothPriorityLevel.PRIORITY_OFF)
diff --git a/acts_tests/acts_contrib/test_utils/cellular/__init__.py b/acts_tests/acts_contrib/test_utils/cellular/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/cellular/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/cellular/cellular_base_test.py b/acts_tests/acts_contrib/test_utils/cellular/cellular_base_test.py
index 8e78a66..f442f98 100644
--- a/acts_tests/acts_contrib/test_utils/cellular/cellular_base_test.py
+++ b/acts_tests/acts_contrib/test_utils/cellular/cellular_base_test.py
@@ -13,7 +13,6 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-import time
 import json
 
 from acts import base_test
@@ -22,14 +21,15 @@
 from acts.controllers.anritsu_lib import md8475_cellular_simulator as anritsu
 from acts.controllers.rohdeschwarz_lib import cmw500_cellular_simulator as cmw
 from acts.controllers.rohdeschwarz_lib import cmx500_cellular_simulator as cmx
+from acts.controllers.uxm_lib import uxm_cellular_simulator as uxm
 from acts.controllers.cellular_lib import AndroidCellularDut
 from acts.controllers.cellular_lib import BaseSimulation as base_sim
 from acts.controllers.cellular_lib import GsmSimulation as gsm_sim
 from acts.controllers.cellular_lib import LteSimulation as lte_sim
 from acts.controllers.cellular_lib import UmtsSimulation as umts_sim
 from acts.controllers.cellular_lib import LteImsSimulation as lteims_sim
+from acts.controllers.cellular_lib import PresetSimulation
 
-from acts_contrib.test_utils.tel import tel_logging_utils
 from acts_contrib.test_utils.tel import tel_test_utils as telutils
 
 
@@ -38,12 +38,13 @@
 
     # List of test name keywords that indicate the RAT to be used
 
-    PARAM_SIM_TYPE_LTE = "lte"
-    PARAM_SIM_TYPE_LTE_CA = "lteca"
-    PARAM_SIM_TYPE_LTE_IMS = "lteims"
-    PARAM_SIM_TYPE_NR = "nr"
-    PARAM_SIM_TYPE_UMTS = "umts"
-    PARAM_SIM_TYPE_GSM = "gsm"
+    PARAM_SIM_TYPE_LTE = 'lte'
+    PARAM_SIM_TYPE_LTE_CA = 'lteca'
+    PARAM_SIM_TYPE_LTE_IMS = 'lteims'
+    PARAM_SIM_TYPE_NR = 'nr'
+    PARAM_SIM_TYPE_UMTS = 'umts'
+    PARAM_SIM_TYPE_GSM = 'gsm'
+    PARAM_SIM_TYPE_PRESET = 'preset'
 
     # Custom files
     FILENAME_CALIBRATION_TABLE_UNFORMATTED = 'calibration_table_{}.json'
@@ -55,7 +56,7 @@
     CALIBRATION_TABLE_FILENAME = 'calibration_table.csv'
 
     def __init__(self, controllers):
-        """ Class initialization.
+        """Class initialization.
 
         Sets class attributes to None.
         """
@@ -68,7 +69,8 @@
         self.test_configs = {}
 
     def setup_class(self):
-        """ Executed before any test case is started.
+        """Executed before any test case is started.
+
         Connects to the cellular instrument.
 
         Returns:
@@ -77,13 +79,13 @@
 
         super().setup_class()
 
-        if not hasattr(self, 'dut'):
-            self.dut = self.android_devices[0]
+        self.cellular_dut = AndroidCellularDut.AndroidCellularDut(
+            self.android_devices[0], self.log)
 
         TEST_PARAMS = self.TAG + '_params'
         self.cellular_test_params = self.user_params.get(TEST_PARAMS, {})
-        self.log.info(
-            'self.cellular_test_params: ' + str(self.cellular_test_params))
+        self.log.info('self.cellular_test_params: ' +
+                      str(self.cellular_test_params))
 
         # Unpack test parameters used in this class
         self.unpack_userparams(['custom_files'],
@@ -93,7 +95,9 @@
                                cmw500_port=None,
                                cmx500_ip=None,
                                cmx500_port=None,
-                               qxdm_logs=None)
+                               modem_logging=None,
+                               uxm_ip=None,
+                               disable_data=None)
 
         # Load calibration tables
         filename_calibration_table = (
@@ -142,7 +146,7 @@
             raise
 
     def initialize_simulator(self):
-        """ Connects to Anritsu Callbox and gets handle object.
+        """Connects to Anritsu Callbox and gets handle object.
 
         Returns:
             False if a connection with the callbox could not be started
@@ -186,13 +190,30 @@
             return cmx.CMX500CellularSimulator(self.cmx500_ip,
                                                self.cmx500_port)
 
+        elif self.uxm_ip:
+            # unpack additional uxm info
+            self.unpack_userparams(uxm_user=None,
+                                   ssh_private_key_to_uxm=None,
+                                   ta_exe_path=None,
+                                   ta_exe_name=None)
+            for param in ('uxm_ip', 'uxm_user', 'ssh_private_key_to_uxm',
+                          'ta_exe_path', 'ta_exe_name', 'custom_files'):
+                if getattr(self, param) is None:
+                    raise RuntimeError('The uxm cellular simulator '
+                                       'requires %s to be set in the '
+                                       'config file.' % param)
+            return uxm.UXMCellularSimulator(self.uxm_ip, self.custom_files,
+                                            self.uxm_user,
+                                            self.ssh_private_key_to_uxm,
+                                            self.ta_exe_path, self.ta_exe_name)
+
         else:
             raise RuntimeError(
                 'The simulator could not be initialized because '
                 'a callbox was not defined in the configs file.')
 
     def setup_test(self):
-        """ Executed before every test case.
+        """Executed before every test case.
 
         Parses parameters from the test name and sets a simulation up according
         to those values. Also takes care of attaching the phone to the base
@@ -229,14 +250,23 @@
             self.init_simulation(self.PARAM_SIM_TYPE_UMTS)
         elif self.consume_parameter(self.PARAM_SIM_TYPE_GSM):
             self.init_simulation(self.PARAM_SIM_TYPE_GSM)
+        elif self.consume_parameter(self.PARAM_SIM_TYPE_PRESET):
+            self.init_simulation(self.PARAM_SIM_TYPE_PRESET)
         else:
             self.log.error(
-                "Simulation type needs to be indicated in the test name.")
+                'Simulation type needs to be indicated in the test name.')
             return False
 
         # Changing cell parameters requires the phone to be detached
         self.simulation.detach()
 
+        # Disable or enable data according to the config. Data is on by default
+        if self.disable_data:
+            self.log.info('disable data for the test')
+        else:
+            self.log.info('enable data for the test')
+        self.cellular_dut.toggle_data(not self.disable_data)
+
         # Configure simulation with parameters loaded from json file
         sim_params = self.test_configs.get(self.test_name)
         if not sim_params:
@@ -258,11 +288,11 @@
         self.log.info('Simulation parameters: ' + str(sim_params))
         self.simulation.configure(sim_params)
 
-        # Enable QXDM logger if required
-        if self.qxdm_logs:
-            self.log.info('Enabling the QXDM logger.')
-            tel_logging_utils.set_qxdm_logger_command(self.dut)
-            tel_logging_utils.start_qxdm_logger(self.dut)
+        if self.modem_logging:
+            try:
+                self.cellular_dut.start_modem_logging()
+            except NotImplementedError:
+                self.log.error('Modem logging couldn\'t be started')
 
         # Start the simulation. This method will raise an exception if
         # the phone is unable to attach.
@@ -271,19 +301,15 @@
         return True
 
     def teardown_test(self):
-        """ Executed after every test case, even if it failed or an exception
-        happened.
+        """Executed after every test case, even if it failed or an exception occur.
 
         Save results to dictionary so they can be displayed after completing
         the test batch.
         """
         super().teardown_test()
 
-        # If QXDM logging was enabled pull the results
-        if self.qxdm_logs:
-            self.log.info('Stopping the QXDM logger and pulling results.')
-            tel_logging_utils.stop_qxdm_logger(self.dut)
-            self.dut.get_qxdm_logs()
+        if self.modem_logging:
+            self.cellular_dut.stop_modem_logging()
 
     def consume_parameter(self, parameter_name, num_values=0):
         """ Parses a parameter from the test name.
@@ -294,7 +320,8 @@
         Args:
             parameter_name: keyword to look up in the test name
             num_values: number of arguments following the parameter name in the
-                test name
+              test name
+
         Returns:
             A list containing the parameter name and the following num_values
             arguments.
@@ -313,7 +340,7 @@
                 return_list.append(self.parameters.pop(i))
         except IndexError:
             self.log.error(
-                "Parameter {} has to be followed by {} values.".format(
+                'Parameter {} has to be followed by {} values.'.format(
                     parameter_name, num_values))
             raise ValueError()
 
@@ -335,7 +362,7 @@
                            'Error message: ' + str(e))
 
     def init_simulation(self, sim_type):
-        """ Starts a new simulation only if needed.
+        """Starts a new simulation only if needed.
 
         Only starts a new simulation if type is different from the one running
         before.
@@ -343,20 +370,27 @@
         Args:
             type: defines the type of simulation to be started.
         """
-
         simulation_dictionary = {
-            self.PARAM_SIM_TYPE_LTE: lte_sim.LteSimulation,
-            self.PARAM_SIM_TYPE_LTE_CA: lte_sim.LteSimulation,
+            self.PARAM_SIM_TYPE_LTE:
+            lte_sim.LteSimulation,
+            self.PARAM_SIM_TYPE_LTE_CA:
+            lte_sim.LteSimulation,
             # The LteSimulation class is able to handle NR cells as well.
             # The long-term goal is to consolidate all simulation classes.
-            self.PARAM_SIM_TYPE_NR: lte_sim.LteSimulation,
-            self.PARAM_SIM_TYPE_UMTS: umts_sim.UmtsSimulation,
-            self.PARAM_SIM_TYPE_GSM: gsm_sim.GsmSimulation,
-            self.PARAM_SIM_TYPE_LTE_IMS: lteims_sim.LteImsSimulation
+            self.PARAM_SIM_TYPE_NR:
+            lte_sim.LteSimulation,
+            self.PARAM_SIM_TYPE_UMTS:
+            umts_sim.UmtsSimulation,
+            self.PARAM_SIM_TYPE_GSM:
+            gsm_sim.GsmSimulation,
+            self.PARAM_SIM_TYPE_LTE_IMS:
+            lteims_sim.LteImsSimulation,
+            self.PARAM_SIM_TYPE_PRESET:
+            PresetSimulation.PresetSimulation,
         }
 
         if not sim_type in simulation_dictionary:
-            raise ValueError("The provided simulation type is invalid.")
+            raise ValueError('The provided simulation type is invalid.')
 
         simulation_class = simulation_dictionary[sim_type]
 
@@ -373,24 +407,27 @@
         if sim_type not in self.calibration_table:
             self.calibration_table[sim_type] = {}
 
-        cellular_dut = AndroidCellularDut.AndroidCellularDut(
-            self.dut, self.log)
         # Instantiate a new simulation
         if sim_type == self.PARAM_SIM_TYPE_NR:
             self.simulation = simulation_class(
                 self.cellular_simulator,
                 self.log,
-                cellular_dut,
+                self.cellular_dut,
                 self.cellular_test_params,
                 self.calibration_table[sim_type],
                 nr_mode=self.PARAM_SIM_TYPE_NR)
-        else:
-            self.simulation = simulation_class(
+        elif sim_type == self.PARAM_SIM_TYPE_PRESET:
+            self.simulation = self.simulation = simulation_class(
                 self.cellular_simulator,
                 self.log,
-                cellular_dut,
+                self.cellular_dut,
                 self.cellular_test_params,
-                self.calibration_table[sim_type])
+                self.calibration_table,
+                nr_mode=self.PARAM_SIM_TYPE_NR)
+        else:
+            self.simulation = simulation_class(
+                self.cellular_simulator, self.log, self.cellular_dut,
+                self.cellular_test_params, self.calibration_table[sim_type])
 
     def ensure_valid_calibration_table(self, calibration_table):
         """ Ensures the calibration table has the correct structure.
diff --git a/acts_tests/acts_contrib/test_utils/cellular/keysight_5g_testapp.py b/acts_tests/acts_contrib/test_utils/cellular/keysight_5g_testapp.py
new file mode 100644
index 0000000..8e4d10a
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/cellular/keysight_5g_testapp.py
@@ -0,0 +1,864 @@
+#!/usr/bin/env python3.4
+#
+#   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 collections
+import pyvisa
+import time
+from acts import logger
+from acts_contrib.test_utils.cellular.performance import cellular_performance_test_utils as cputils
+
+SHORT_SLEEP = 1
+VERY_SHORT_SLEEP = 0.1
+SUBFRAME_DURATION = 0.001
+VISA_QUERY_DELAY = 0.01
+
+
+class Keysight5GTestApp(object):
+    """Controller for the Keysight 5G NR Test Application.
+
+    This controller enables interacting with a Keysight Test Application
+    running on a remote test PC and implements many of the configuration
+    parameters supported in test app.
+    """
+
+    VISA_LOCATION = '/opt/keysight/iolibs/libktvisa32.so'
+
+    def __init__(self, config):
+        self.config = config
+        self.log = logger.create_tagged_trace_logger("{}{}".format(
+            self.config['brand'], self.config['model']))
+        self.resource_manager = pyvisa.ResourceManager(self.VISA_LOCATION)
+        self.test_app = self.resource_manager.open_resource(
+            'TCPIP0::{}::{}::INSTR'.format(self.config['ip_address'],
+                                           self.config['hislip_interface']))
+        self.test_app.timeout = 200000
+        self.test_app.write_termination = '\n'
+        self.test_app.read_termination = '\n'
+        self.test_app.query_delay = VISA_QUERY_DELAY
+        self.last_loaded_scpi = None
+
+        inst_id = self.send_cmd('*IDN?', 1)
+        if 'Keysight' not in inst_id[0]:
+            self.log.error(
+                'Failed to connect to Keysight Test App: {}'.format(inst_id))
+        else:
+            self.log.info("Test App ID: {}".format(inst_id))
+
+    def destroy(self):
+        self.test_app.close()
+
+    ### Programming Utilities
+    @staticmethod
+    def _format_cells(cells):
+        "Helper function to format list of cells."
+        if isinstance(cells, int):
+            return 'CELL{}'.format(cells)
+        elif isinstance(cells, str):
+            return cells
+        elif isinstance(cells, list):
+            cell_list = [
+                Keysight5GTestApp._format_cells(cell) for cell in cells
+            ]
+            cell_list = ','.join(cell_list)
+            return cell_list
+
+    @staticmethod
+    def _format_response(response):
+        "Helper function to format test app response."
+
+        def _format_response_entry(entry):
+            try:
+                formatted_entry = float(entry)
+            except:
+                formatted_entry = entry
+            return formatted_entry
+
+        if ',' not in response:
+            return _format_response_entry(response)
+        response = response.split(',')
+        formatted_response = [
+            _format_response_entry(entry) for entry in response
+        ]
+        return formatted_response
+
+    def send_cmd(self, command, read_response=0, check_errors=1):
+        "Helper function to write to or query test app."
+        if read_response:
+            try:
+                response = Keysight5GTestApp._format_response(
+                    self.test_app.query(command))
+                time.sleep(VISA_QUERY_DELAY)
+                if check_errors:
+                    error = self.test_app.query('SYSTem:ERRor?')
+                    time.sleep(VISA_QUERY_DELAY)
+                    if 'No error' not in error:
+                        self.log.warning("Command: {}. Error: {}".format(
+                            command, error))
+                return response
+            except:
+                raise RuntimeError('Lost connection to test app.')
+        else:
+            try:
+                self.test_app.write(command)
+                time.sleep(VISA_QUERY_DELAY)
+                if check_errors:
+                    error = self.test_app.query('SYSTem:ERRor?')
+                    if 'No error' not in error:
+                        self.log.warning("Command: {}. Error: {}".format(
+                            command, error))
+                self.send_cmd('*OPC?', 1)
+                time.sleep(VISA_QUERY_DELAY)
+            except:
+                raise RuntimeError('Lost connection to test app.')
+            return None
+
+    def import_scpi_file(self, file_name, check_last_loaded=0):
+        """Function to import SCPI file specified in file_name.
+
+        Args:
+            file_name: name of SCPI file to run
+            check_last_loaded: flag to check last loaded scpi and
+            only load if different.
+        """
+        if file_name == self.last_loaded_scpi and check_last_loaded:
+            self.log.info('Skipping SCPI import.')
+        self.send_cmd("SYSTem:SCPI:IMPort '{}'".format(file_name))
+        while int(self.send_cmd('SYSTem:SCPI:IMPort:STATus?', 1)):
+            self.send_cmd('*OPC?', 1)
+        self.log.info('Done with SCPI import')
+
+    ### Configure Cells
+    def assert_cell_off_decorator(func):
+        "Decorator function that ensures cells or off when configuring them"
+
+        def inner(self, *args, **kwargs):
+            if "nr" in func.__name__:
+                cell_type = 'NR5G'
+            else:
+                cell_type = kwargs.get('cell_type', args[0])
+            cell = kwargs.get('cell', args[1])
+            cell_state = self.get_cell_state(cell_type, cell)
+            if cell_state:
+                self.log.error('Cell must be off when calling {}'.format(
+                    func.__name__))
+            return (func(self, *args, **kwargs))
+
+        return inner
+
+    def assert_cell_off(self, cell_type, cell):
+        cell_state = self.get_cell_state(cell_type, cell)
+        if cell_state:
+            self.log.error('Cell must be off')
+
+    def select_cell(self, cell_type, cell):
+        """Function to select active cell.
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+        """
+        self.send_cmd('BSE:SELected:CELL {},{}'.format(
+            cell_type, Keysight5GTestApp._format_cells(cell)))
+
+    def select_display_tab(self, cell_type, cell, tab, subtab):
+        """Function to select display tab.
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            tab: tab to display for the selected cell
+        """
+        supported_tabs = {
+            'PHY': [
+                'BWP', 'HARQ', 'PDSCH', 'PDCCH', 'PRACH', 'PUSCH', 'PUCCH',
+                'SRSC'
+            ],
+            'BTHR': ['SUMMARY', 'OTAGRAPH', 'ULOTA', 'DLOTA'],
+            'CSI': []
+        }
+        if (tab not in supported_tabs) or (subtab not in supported_tabs[tab]):
+            return
+        self.select_cell(cell_type, cell)
+        self.send_cmd('DISPlay:{} {},{}'.format(cell_type, tab, subtab))
+
+    def get_cell_state(self, cell_type, cell):
+        """Function to get cell on/off state.
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+        Returns:
+            cell_state: boolean. True if cell on
+        """
+        cell_state = int(
+            self.send_cmd(
+                'BSE:CONFig:{}:{}:ACTive:STATe?'.format(
+                    cell_type, Keysight5GTestApp._format_cells(cell)), 1))
+        return cell_state
+
+    def wait_for_cell_status(self,
+                             cell_type,
+                             cell,
+                             states,
+                             timeout,
+                             polling_interval=SHORT_SLEEP):
+        """Function to wait for a specific cell status
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            states: list of acceptable states (ON, CONN, AGG, ACT, etc)
+            timeout: amount of time to wait for requested status
+        Returns:
+            True if one of the listed states is achieved
+            False if timed out waiting for acceptable state.
+        """
+        states = [states] if isinstance(states, str) else states
+        for i in range(int(timeout / polling_interval)):
+            current_state = self.send_cmd(
+                'BSE:STATus:{}:{}?'.format(
+                    cell_type, Keysight5GTestApp._format_cells(cell)), 1)
+            if current_state in states:
+                return True
+            time.sleep(polling_interval)
+        self.log.warning('Timeout waiting for {} {} {}'.format(
+            cell_type, Keysight5GTestApp._format_cells(cell), states))
+        return False
+
+    def set_cell_state(self, cell_type, cell, state):
+        """Function to set cell state
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            state: requested state
+        """
+        self.send_cmd('BSE:CONFig:{}:{}:ACTive:STATe {}'.format(
+            cell_type, Keysight5GTestApp._format_cells(cell), state))
+
+    def set_cell_type(self, cell_type, cell, sa_or_nsa):
+        """Function to set cell duplex mode
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            sa_or_nsa: SA or NSA
+        """
+        self.assert_cell_off(cell_type, cell)
+        self.send_cmd('BSE: CONFig:NR5G:{}:TYPE {}'.format(
+            Keysight5GTestApp._format_cells(cell), sa_or_nsa))
+
+    def set_cell_duplex_mode(self, cell_type, cell, duplex_mode):
+        """Function to set cell duplex mode
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            duplex_mode: TDD or FDD
+        """
+        self.assert_cell_off(cell_type, cell)
+        self.send_cmd('BSE:CONFig:{}:{}:DUPLEX:MODe {}'.format(
+            cell_type, Keysight5GTestApp._format_cells(cell), duplex_mode))
+
+    def set_cell_band(self, cell_type, cell, band):
+        """Function to set cell band
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            band: LTE or NR band (e.g. 1,3,N260, N77)
+        """
+        self.assert_cell_off(cell_type, cell)
+        self.send_cmd('BSE:CONFig:{}:{}:BAND {}'.format(
+            cell_type, Keysight5GTestApp._format_cells(cell), band))
+
+    def set_cell_channel(self, cell_type, cell, channel, arfcn=1):
+        """Function to set cell frequency/channel
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            channel: requested channel (ARFCN) or frequency in MHz
+        """
+        self.assert_cell_off(cell_type, cell)
+        if cell_type == 'NR5G' and isinstance(
+                channel, str) and channel.lower() in ['low', 'mid', 'high']:
+            self.send_cmd('BSE:CONFig:{}:{}:TESTChanLoc {}'.format(
+                cell_type, Keysight5GTestApp._format_cells(cell), channel.upper()))
+        elif arfcn == 1:
+            self.send_cmd('BSE:CONFig:{}:{}:DL:CHANnel {}'.format(
+                cell_type, Keysight5GTestApp._format_cells(cell), channel))
+        else:
+            self.send_cmd('BSE:CONFig:{}:{}:DL:FREQuency:MAIN {}'.format(
+                cell_type, Keysight5GTestApp._format_cells(cell),
+                channel * 1e6))
+
+    def toggle_contiguous_nr_channels(self, force_contiguous):
+        self.assert_cell_off('NR5G', 1)
+        self.log.warning('Forcing contiguous NR channels overrides channel config.')
+        self.send_cmd('BSE:CONFig:NR5G:PHY:OPTimize:CONTiguous:STATe 0')
+        if force_contiguous:
+            self.send_cmd('BSE:CONFig:NR5G:PHY:OPTimize:CONTiguous:STATe 1')
+
+    def configure_contiguous_nr_channels(self, cell, band, channel):
+        """Function to set cell frequency/channel
+
+        Args:
+            cell: cell/carrier number
+            band: band to set channel in (only required for preset)
+            channel_preset: frequency in MHz or preset in [low, mid, or high]
+        """
+        self.assert_cell_off('NR5G', cell)
+        self.send_cmd('BSE:CONFig:NR5G:PHY:OPTimize:CONTiguous:STATe 0')
+        if channel.lower() in ['low', 'mid', 'high']:
+            pcc_arfcn = cputils.PCC_PRESET_MAPPING[band][channel]
+            self.set_cell_channel('NR5G', cell, pcc_arfcn, 1)
+        else:
+            self.set_cell_channel('NR5G', cell, channel, 0)
+        self.send_cmd('BSE:CONFig:NR5G:PHY:OPTimize:CONTiguous:STATe 1')
+
+    def configure_noncontiguous_nr_channels(self, cells, band, channels):
+        """Function to set cell frequency/channel
+
+        Args:
+            cell: cell/carrier number
+            band: band number
+            channel: frequency in MHz
+        """
+        for cell in cells:
+            self.assert_cell_off('NR5G', cell)
+        self.send_cmd('BSE:CONFig:NR5G:PHY:OPTimize:CONTiguous:STATe 0')
+        for cell, channel in zip(cells, channels):
+            self.set_cell_channel('NR5G', cell, channel, arfcn=0)
+
+    def set_cell_bandwidth(self, cell_type, cell, bandwidth):
+        """Function to set cell bandwidth
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            bandwidth: requested bandwidth
+        """
+        self.assert_cell_off(cell_type, cell)
+        self.send_cmd('BSE:CONFig:{}:{}:DL:BW {}'.format(
+            cell_type, Keysight5GTestApp._format_cells(cell), bandwidth))
+
+    def set_nr_subcarrier_spacing(self, cell, subcarrier_spacing):
+        """Function to set cell bandwidth
+
+        Args:
+            cell: cell/carrier number
+            subcarrier_spacing: requested SCS
+        """
+        self.assert_cell_off('NR5G', cell)
+        self.send_cmd('BSE:CONFig:NR5G:{}:SUBCarrier:SPACing:COMMon {}'.format(
+            Keysight5GTestApp._format_cells(cell), subcarrier_spacing))
+
+    def set_cell_mimo_config(self, cell_type, cell, link, mimo_config):
+        """Function to set cell mimo config.
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            link: uplink or downlink
+            mimo_config: requested mimo configuration (refer to SCPI
+                         documentation for allowed range of values)
+        """
+        self.assert_cell_off(cell_type, cell)
+        if cell_type == 'NR5G':
+            self.send_cmd('BSE:CONFig:{}:{}:{}:MIMO:CONFig {}'.format(
+                cell_type, Keysight5GTestApp._format_cells(cell), link,
+                mimo_config))
+        else:
+            self.send_cmd('BSE:CONFig:{}:{}:PHY:DL:ANTenna:CONFig {}'.format(
+                cell_type, Keysight5GTestApp._format_cells(cell), mimo_config))
+
+    def set_lte_cell_transmission_mode(self, cell, transmission_mode):
+        """Function to set LTE cell transmission mode.
+
+        Args:
+            cell: cell/carrier number
+            transmission_mode: one of TM1, TM2, TM3, TM4 ...
+        """
+        self.assert_cell_off('LTE', cell)
+        self.send_cmd('BSE:CONFig:LTE:{}:RRC:TMODe {}'.format(
+            Keysight5GTestApp._format_cells(cell), transmission_mode))
+
+    def set_cell_dl_power(self, cell_type, cell, power, full_bw):
+        """Function to set cell power
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            power: requested power
+            full_bw: boolean controlling if requested power is per channel
+                     or subcarrier
+        """
+        if full_bw:
+            self.send_cmd('BSE:CONFIG:{}:{}:DL:POWer:CHANnel {}'.format(
+                cell_type, Keysight5GTestApp._format_cells(cell), power))
+        else:
+            self.send_cmd('BSE:CONFIG:{}:{}:DL:POWer:EPRE {}'.format(
+                cell_type, Keysight5GTestApp._format_cells(cell), power))
+        self.send_cmd('BSE:CONFig:{}:APPLY'.format(cell_type))
+
+    def set_cell_duplex_mode(self, cell_type, cell, duplex_mode):
+        """Function to set cell power
+
+        Args:
+            cell_type: LTE or NR5G cell
+            cell: cell/carrier number
+            duplex mode: TDD or FDD
+        """
+        self.assert_cell_off(cell_type, cell)
+        self.send_cmd('BSE:CONFig:{}:{}:DUPLEX:MODe {}'.format(
+            cell_type, Keysight5GTestApp._format_cells(cell), duplex_mode))
+
+    def set_dl_carriers(self, cells):
+        """Function to set aggregated DL NR5G carriers
+
+        Args:
+            cells: list of DL cells/carriers to aggregate with LTE (e.g. [1,2])
+        """
+        self.send_cmd('BSE:CONFig:NR5G:CELL1:CAGGregation:NRCC:DL {}'.format(
+            Keysight5GTestApp._format_cells(cells)))
+
+    def set_ul_carriers(self, cells):
+        """Function to set aggregated UL NR5G carriers
+
+        Args:
+            cells: list of DL cells/carriers to aggregate with LTE (e.g. [1,2])
+        """
+        self.send_cmd('BSE:CONFig:NR5G:CELL1:CAGGregation:NRCC:UL {}'.format(
+            Keysight5GTestApp._format_cells(cells)))
+
+    def set_nr_cell_schedule_scenario(self, cell, scenario):
+        """Function to set NR schedule to one of predefince quick configs.
+
+        Args:
+            cell: cell number to address. schedule will apply to all cells
+            scenario: one of the predefined test app schedlue quick configs
+                      (e.g. FULL_TPUT, BASIC).
+        """
+        self.assert_cell_off('NR5G', cell)
+        self.send_cmd(
+            'BSE:CONFig:NR5G:{}:SCHeduling:QCONFig:SCENario {}'.format(
+                Keysight5GTestApp._format_cells(cell), scenario))
+        self.send_cmd('BSE:CONFig:NR5G:SCHeduling:QCONFig:APPLy:ALL')
+
+    def set_nr_cell_mcs(self, cell, dl_mcs, ul_mcs):
+        """Function to set NR cell DL & UL MCS
+
+        Args:
+            cell: cell number to address. MCS will apply to all cells
+            dl_mcs: mcs index to use on DL
+            ul_mcs: mcs index to use on UL
+        """
+        self.assert_cell_off('NR5G', cell)
+        self.send_cmd(
+            'BSE:CONFig:NR5G:SCHeduling:SETParameter "CELLALL:BWPALL:FCALL:SCALL", "DL:IMCS", "{}"'
+            .format(dl_mcs))
+        self.send_cmd(
+            'BSE:CONFig:NR5G:SCHeduling:SETParameter "CELLALL:BWPALL:FCALL:SCALL", "UL:IMCS", "{}"'
+            .format(ul_mcs))
+
+    def set_lte_cell_mcs(
+        self,
+        cell,
+        dl_mcs_table,
+        dl_mcs,
+        ul_mcs_table,
+        ul_mcs,
+    ):
+        """Function to set NR cell DL & UL MCS
+
+        Args:
+            cell: cell number to address. MCS will apply to all cells
+            dl_mcs: mcs index to use on DL
+            ul_mcs: mcs index to use on UL
+        """
+        if dl_mcs_table == 'QAM256':
+            dl_mcs_table_formatted = 'ASUBframe'
+        elif dl_mcs_table == 'QAM1024':
+            dl_mcs_table_formatted = 'ASUB1024'
+        elif dl_mcs_table == 'QAM64':
+            dl_mcs_table_formatted = 'DISabled'
+        self.assert_cell_off('LTE', cell)
+        self.send_cmd(
+            'BSE:CONFig:LTE:SCHeduling:SETParameter "CELLALL", "DL:MCS:TABle", "{}"'
+            .format(dl_mcs_table_formatted))
+        self.send_cmd(
+            'BSE:CONFig:LTE:SCHeduling:SETParameter "CELLALL:SFALL:CWALL", "DL:IMCS", "{}"'
+            .format(dl_mcs))
+        self.send_cmd(
+            'BSE:CONFig:LTE:SCHeduling:SETParameter "CELLALL", "UL:MCS:TABle", "{}"'
+            .format(ul_mcs_table))
+        self.send_cmd(
+            'BSE:CONFig:LTE:SCHeduling:SETParameter "CELLALL:SFALL", "UL:IMCS", "{}"'
+            .format(ul_mcs))
+
+    def set_lte_control_region_size(self, cell, num_symbols):
+        self.assert_cell_off('LTE', cell)
+        self.send_cmd('BSE:CONFig:LTE:{}:PHY:PCFich:CFI {}'.format(
+            Keysight5GTestApp._format_cells(cell), num_symbols))
+
+    def set_lte_ul_mac_padding(self, mac_padding):
+        self.assert_cell_off('LTE', 'CELL1')
+        padding_str = 'TRUE' if mac_padding else 'FALSE'
+        self.send_cmd(
+            'BSE:CONFig:LTE:SCHeduling:SETParameter "CELLALL", "UL:MAC:PADDING", "{}"'
+            .format(padding_str))
+
+    def set_nr_ul_dft_precoding(self, cell, precoding):
+        """Function to configure DFT-precoding on uplink.
+
+        Args:
+            cell: cell number to address. MCS will apply to all cells
+            precoding: 0/1 to disable/enable precoding
+        """
+        self.assert_cell_off('NR5G', cell)
+        precoding_str = "ENABled" if precoding else "DISabled"
+        self.send_cmd(
+            'BSE:CONFig:NR5G:{}:SCHeduling:QCONFig:UL:TRANsform:PRECoding {}'.
+            format(Keysight5GTestApp._format_cells(cell), precoding_str))
+        precoding_str = "True" if precoding else "False"
+        self.send_cmd(
+            'BSE:CONFig:NR5G:SCHeduling:SETParameter "CELLALL:BWPALL", "UL:TPEnabled", "{}"'
+            .format(precoding_str))
+
+    def configure_ul_clpc(self, channel, mode, target):
+        """Function to configure UL power control on all cells/carriers
+
+        Args:
+            channel: physical channel must be PUSCh or PUCCh
+            mode: mode supported by test app (all up/down bits, target, etc)
+            target: target power if mode is set to target
+        """
+        self.send_cmd('BSE:CONFig:NR5G:UL:{}:CLPControl:MODE:ALL {}'.format(
+            channel, mode))
+        if "tar" in mode.lower():
+            self.send_cmd(
+                'BSE:CONFig:NR5G:UL:{}:CLPControl:TARGet:POWer:ALL {}'.format(
+                    channel, target))
+
+    def apply_lte_carrier_agg(self, cells):
+        """Function to start LTE carrier aggregation on already configured cells"""
+        if self.wait_for_cell_status('LTE', 'CELL1', 'CONN', 60):
+            self.send_cmd(
+                'BSE:CONFig:LTE:CELL1:CAGGregation:AGGRegate:SCC {}'.format(
+                    Keysight5GTestApp._format_cells(cells)))
+            self.send_cmd(
+                'BSE:CONFig:LTE:CELL1:CAGGregation:ACTivate:SCC {}'.format(
+                    Keysight5GTestApp._format_cells(cells)))
+
+    def apply_carrier_agg(self):
+        """Function to start carrier aggregation on already configured cells"""
+        if self.wait_for_cell_status('LTE', 'CELL1', 'CONN', 60):
+            self.send_cmd(
+                'BSE:CONFig:LTE:CELL1:CAGGregation:AGGRegate:NRCC:APPly')
+        else:
+            raise RuntimeError('LTE must be connected to start aggregation.')
+
+    def get_ip_throughput(self, cell_type):
+        """Function to query IP layer throughput on LTE or NR
+
+        Args:
+            cell_type: LTE or NR5G
+        Returns:
+            dict containing DL and UL IP-layer throughput
+        """
+        #Tester reply format
+        #{ report-count, total-bytes, current transfer-rate, average transfer-rate, peak transfer-rate }
+        dl_tput = self.send_cmd(
+            'BSE:MEASure:{}:BTHRoughput:DL:THRoughput:IP?'.format(cell_type),
+            1)
+        ul_tput = self.send_cmd(
+            'BSE:MEASure:{}:BTHRoughput:UL:THRoughput:IP?'.format(cell_type),
+            1)
+        return {'dl_tput': dl_tput, 'ul_tput': ul_tput}
+
+    def _get_throughput(self, cell_type, link, cell):
+        """Helper function to get PHY layer throughput on single cell"""
+        if cell_type == 'LTE':
+            tput_response = self.send_cmd(
+                'BSE:MEASure:LTE:{}:BTHRoughput:{}:THRoughput:OTA:{}?'.format(
+                    Keysight5GTestApp._format_cells(cell), link,
+                    Keysight5GTestApp._format_cells(cell)), 1)
+        elif cell_type == 'NR5G':
+            # Tester reply format
+            #progress-count, ack-count, ack-ratio, nack-count, nack-ratio,  statdtx-count,  statdtx-ratio,  pdschBlerCount,  pdschBlerRatio,  pdschTputRatio.
+            tput_response = self.send_cmd(
+                'BSE:MEASure:NR5G:BTHRoughput:{}:THRoughput:OTA:{}?'.format(
+                    link, Keysight5GTestApp._format_cells(cell)), 1)
+        tput_result = {
+            'frame_count': tput_response[0] / 1e6,
+            'current_tput': tput_response[1] / 1e6,
+            'min_tput': tput_response[2] / 1e6,
+            'max_tput': tput_response[3] / 1e6,
+            'average_tput': tput_response[4] / 1e6,
+            'theoretical_tput': tput_response[5] / 1e6,
+        }
+        return tput_result
+
+    def get_throughput(self, cell_type, cells):
+        """Function to get PHY layer throughput on on or more cells
+
+        This function returns the throughput data on the requested cells
+        during the last BLER test run, i.e., throughpt data must be fetch at
+        the end/after a BLE test run on the Keysight Test App.
+
+        Args:
+            cell_type: LTE or NR5G
+            cells: list of cells to query for throughput data
+        Returns:
+            tput_result: dict containing all throughput statistics in Mbps
+        """
+        if not isinstance(cells, list):
+            cells = [cells]
+        tput_result = collections.OrderedDict()
+        for cell in cells:
+            tput_result[cell] = {
+                'DL': self._get_throughput(cell_type, 'DL', cell),
+                'UL': self._get_throughput(cell_type, 'UL', cell)
+            }
+            frame_count = tput_result[cell]['DL']['frame_count']
+        agg_tput = {
+            'DL': {
+                'frame_count': frame_count,
+                'current_tput': 0,
+                'min_tput': 0,
+                'max_tput': 0,
+                'average_tput': 0,
+                'theoretical_tput': 0
+            },
+            'UL': {
+                'frame_count': frame_count,
+                'current_tput': 0,
+                'min_tput': 0,
+                'max_tput': 0,
+                'average_tput': 0,
+                'theoretical_tput': 0
+            }
+        }
+        for cell, cell_tput in tput_result.items():
+            for link, link_tput in cell_tput.items():
+                for key, value in link_tput.items():
+                    if 'tput' in key:
+                        agg_tput[link][key] = agg_tput[link][key] + value
+        tput_result['total'] = agg_tput
+        return tput_result
+
+    def _clear_bler_measurement(self, cell_type):
+        """Helper function to clear BLER results."""
+        if cell_type == 'LTE':
+            self.send_cmd('BSE:MEASure:LTE:CELL1:BTHRoughput:CLEar')
+        elif cell_type == 'NR5G':
+            self.send_cmd('BSE:MEASure:NR5G:BTHRoughput:CLEar')
+
+    def _configure_bler_measurement(self, cell_type, continuous, length):
+        """Helper function to configure BLER results."""
+        if continuous:
+            if cell_type == 'LTE':
+                self.send_cmd('BSE:MEASure:LTE:CELL1:BTHRoughput:CONTinuous 1')
+            elif cell_type == 'NR5G':
+                self.send_cmd('BSE:MEASure:NR5G:BTHRoughput:CONTinuous 1')
+        elif length > 1:
+            if cell_type == 'LTE':
+                self.send_cmd(
+                    'BSE:MEASure:LTE:CELL1:BTHRoughput:LENGth {}'.format(
+                        length))
+                self.send_cmd('BSE:MEASure:LTE:CELL1:BTHRoughput:CONTinuous 0')
+            elif cell_type == 'NR5G':
+                self.send_cmd(
+                    'BSE:MEASure:NR5G:BTHRoughput:LENGth {}'.format(length))
+                self.send_cmd('BSE:MEASure:NR5G:BTHRoughput:CONTinuous 0')
+
+    def _set_bler_measurement_state(self, cell_type, state):
+        """Helper function to start or stop BLER measurement."""
+        if cell_type == 'LTE':
+            self.send_cmd(
+                'BSE:MEASure:LTE:CELL1:BTHRoughput:STATe {}'.format(state))
+        elif cell_type == 'NR5G':
+            self.send_cmd(
+                'BSE:MEASure:NR5G:BTHRoughput:STATe {}'.format(state))
+
+    def start_bler_measurement(self, cell_type, cells, length):
+        """Function to kick off a BLER measurement
+
+        Args:
+            cell_type: LTE or NR5G
+            length: integer length of BLER measurements in subframes
+        """
+        self._clear_bler_measurement(cell_type)
+        self._set_bler_measurement_state(cell_type, 0)
+        self._configure_bler_measurement(cell_type, 0, length)
+        self._set_bler_measurement_state(cell_type, 1)
+        time.sleep(0.1)
+        bler_check = self.get_bler_result(cell_type, cells, length, 0)
+        if bler_check['total']['DL']['frame_count'] == 0:
+            self.log.warning('BLER measurement did not start. Retrying')
+            self.start_bler_measurement(cell_type, cells, length)
+
+    def _get_bler(self, cell_type, link, cell):
+        """Helper function to get single-cell BLER measurement results."""
+        if cell_type == 'LTE':
+            bler_response = self.send_cmd(
+                'BSE:MEASure:LTE:CELL1:BTHRoughput:{}:BLER:CELL1?'.format(
+                    link), 1)
+        elif cell_type == 'NR5G':
+            bler_response = self.send_cmd(
+                'BSE:MEASure:NR5G:BTHRoughput:{}:BLER:{}?'.format(
+                    link, Keysight5GTestApp._format_cells(cell)), 1)
+        bler_result = {
+            'frame_count': bler_response[0],
+            'ack_count': bler_response[1],
+            'ack_ratio': bler_response[2],
+            'nack_count': bler_response[3],
+            'nack_ratio': bler_response[4]
+        }
+        return bler_result
+
+    def get_bler_result(self,
+                        cell_type,
+                        cells,
+                        length,
+                        wait_for_length=1,
+                        polling_interval=SHORT_SLEEP):
+        """Function to get BLER results.
+
+        This function gets the BLER measurements results on one or more
+        requested cells. The function can either return BLER statistics
+        immediately or wait until a certain number of subframes have been
+        counted (e.g. if the BLER measurement is done)
+
+        Args:
+            cell_type: LTE or NR5G
+            cells: list of cells for which to get BLER
+            length: number of subframes to wait for (typically set to the
+                    configured length of the BLER measurements)
+            wait_for_length: boolean to block/wait till length subframes have
+            been counted.
+        Returns:
+            bler_result: dict containing per-cell and aggregate BLER results
+        """
+
+        if not isinstance(cells, list):
+            cells = [cells]
+        while wait_for_length:
+            dl_bler = self._get_bler(cell_type, 'DL', cells[0])
+            if dl_bler['frame_count'] < length:
+                time.sleep(polling_interval)
+            else:
+                break
+
+        bler_result = collections.OrderedDict()
+        for cell in cells:
+            bler_result[cell] = {
+                'DL': self._get_bler(cell_type, 'DL', cell),
+                'UL': self._get_bler(cell_type, 'UL', cell)
+            }
+        agg_bler = {
+            'DL': {
+                'frame_count': length,
+                'ack_count': 0,
+                'ack_ratio': 0,
+                'nack_count': 0,
+                'nack_ratio': 0
+            },
+            'UL': {
+                'frame_count': length,
+                'ack_count': 0,
+                'ack_ratio': 0,
+                'nack_count': 0,
+                'nack_ratio': 0
+            }
+        }
+        for cell, cell_bler in bler_result.items():
+            for link, link_bler in cell_bler.items():
+                for key, value in link_bler.items():
+                    if 'ack_count' in key:
+                        agg_bler[link][key] = agg_bler[link][key] + value
+        dl_ack_nack = agg_bler['DL']['ack_count'] + agg_bler['DL']['nack_count']
+        ul_ack_nack = agg_bler['UL']['ack_count'] + agg_bler['UL']['nack_count']
+        try:
+            agg_bler['DL'][
+                'ack_ratio'] = agg_bler['DL']['ack_count'] / dl_ack_nack
+            agg_bler['DL'][
+                'nack_ratio'] = agg_bler['DL']['nack_count'] / dl_ack_nack
+            agg_bler['UL'][
+                'ack_ratio'] = agg_bler['UL']['ack_count'] / ul_ack_nack
+            agg_bler['UL'][
+                'nack_ratio'] = agg_bler['UL']['nack_count'] / ul_ack_nack
+        except:
+            self.log.debug(bler_result)
+            agg_bler['DL']['ack_ratio'] = 0
+            agg_bler['DL']['nack_ratio'] = 1
+            agg_bler['UL']['ack_ratio'] = 0
+            agg_bler['UL']['nack_ratio'] = 1
+        bler_result['total'] = agg_bler
+        return bler_result
+
+    def measure_bler(self, cell_type, cells, length):
+        """Function to start and wait for BLER results.
+
+        This function starts a BLER test on a number of cells and waits for the
+        test to complete before returning the BLER measurements.
+
+        Args:
+            cell_type: LTE or NR5G
+            cells: list of cells for which to get BLER
+            length: number of subframes to wait for (typically set to the
+                    configured length of the BLER measurements)
+        Returns:
+            bler_result: dict containing per-cell and aggregate BLER results
+        """
+        self.start_bler_measurement(cell_type, cells, length)
+        time.sleep(length * SUBFRAME_DURATION)
+        bler_result = self.get_bler_result(cell_type, cells, length, 1)
+        return bler_result
+
+    def start_nr_rsrp_measurement(self, cells, length):
+        """Function to start 5G NR RSRP measurement.
+
+        Args:
+            cells: list of NR cells to get RSRP on
+            length: length of RSRP measurement in milliseconds
+        Returns:
+            rsrp_result: dict containing per-cell and aggregate BLER results
+        """
+        for cell in cells:
+            self.send_cmd('BSE:MEASure:NR5G:{}:L1:RSRPower:STOP'.format(
+                Keysight5GTestApp._format_cells(cell)))
+        for cell in cells:
+            self.send_cmd('BSE:MEASure:NR5G:{}:L1:RSRPower:LENGth {}'.format(
+                Keysight5GTestApp._format_cells(cell), length))
+        for cell in cells:
+            self.send_cmd('BSE:MEASure:NR5G:{}:L1:RSRPower:STARt'.format(
+                Keysight5GTestApp._format_cells(cell)))
+
+    def get_nr_rsrp_measurement_state(self, cells):
+        for cell in cells:
+            self.log.info(
+                self.send_cmd(
+                    'BSE:MEASure:NR5G:{}:L1:RSRPower:STATe?'.format(
+                        Keysight5GTestApp._format_cells(cell)), 1))
+
+    def get_nr_rsrp_measurement_results(self, cells):
+        for cell in cells:
+            self.log.info(
+                self.send_cmd(
+                    'BSE:MEASure:NR5G:{}:L1:RSRPower:REPorts:JSON?'.format(
+                        Keysight5GTestApp._format_cells(cell)), 1))
diff --git a/acts_tests/acts_contrib/test_utils/cellular/keysight_catr_chamber.py b/acts_tests/acts_contrib/test_utils/cellular/keysight_catr_chamber.py
new file mode 100644
index 0000000..bd7540d
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/cellular/keysight_catr_chamber.py
@@ -0,0 +1,181 @@
+# Copyright 2020 Google Inc. All Rights Reserved.
+# Author: oelayach@google.com
+
+import pyvisa
+import time
+from acts import logger
+from ota_chamber import Chamber
+
+
+class Chamber(Chamber):
+    """Base class implementation for signal generators.
+
+    Base class provides functions whose implementation is shared by all
+    chambers.
+    """
+    CHAMBER_SLEEP = 10
+
+    def __init__(self, config):
+        self.config = config
+        self.log = logger.create_tagged_trace_logger("{}{}".format(
+            self.config['brand'], self.config['model']))
+        self.chamber_resource = pyvisa.ResourceManager()
+        self.chamber_inst = self.chamber_resource.open_resource(
+            '{}::{}::{}::INSTR'.format(self.config['network_id'],
+                                       self.config['ip_address'],
+                                       self.config['hislip_interface']))
+        self.chamber_inst.timeout = 200000
+        self.chamber_inst.write_termination = '\n'
+        self.chamber_inst.read_termination = '\n'
+
+        self.id_check(self.config)
+        self.current_azim = 0
+        self.current_roll = 0
+        if self.config.get('reset_home', True):
+            self.find_chamber_home()
+            self.move_theta_phi_abs(self.config['chamber_home']['theta'],
+                                    self.config['chamber_home']['phi'])
+            self.set_new_home_position()
+        else:
+            self.config['chamber_home'] = {'phi': 0, 'theta': 0}
+            self.log.warning(
+                'Reset home set to false. Assumed [0,0]. Chamber angles may not be as expected.'
+            )
+
+    def id_check(self, config):
+        """ Checks Chamber ID."""
+        self.log.info("ID Check Successful.")
+        self.log.info(self.chamber_inst.query("*IDN?"))
+
+    def reset(self):
+        self.reset_phi_theta()
+
+    def disconnect(self):
+        if self.config.get('reset_home', True):
+            self.reset_phi_theta()
+        self.chamber_inst.close()
+        self.chamber_resource.close()
+
+    def find_chamber_home(self):
+        self.chamber_inst.write(f"POS:BOR:INIT")
+        self.set_new_home_position()
+        self.wait_for_move_end()
+
+    def set_new_home_position(self):
+        self.chamber_inst.write(f"POS:ZERO:RES")
+
+    def get_phi(self):
+        return self.current_azim
+
+    def get_theta(self):
+        return self.current_roll
+
+    def get_pattern_sweep_limits(self):
+        return {
+            "pattern_phi_start": -self.config['chamber_home']['phi'],
+            "pattern_phi_stop": 165 - self.config['chamber_home']['phi'],
+            "pattern_theta_start": -self.config['chamber_home']['theta'],
+            "pattern_theta_stop": 360 - self.config['chamber_home']['theta'],
+        }
+
+    def move_phi_abs(self, phi):
+        self.log.info("Moving to Phi={}".format(phi))
+        self.move_to_azim_roll(phi, self.current_roll)
+
+    def move_theta_abs(self, theta):
+        self.log.info("Moving to Theta={}".format(theta))
+        self.move_to_azim_roll(self.current_azim, theta)
+
+    def move_theta_phi_abs(self, theta, phi):
+        self.log.info("Moving chamber to [{}, {}]".format(theta, phi))
+        self.move_to_azim_roll(phi, theta)
+
+    def move_phi_rel(self, phi):
+        self.log.info("Moving Phi by {} degrees".format(phi))
+        self.move_to_azim_roll(self.current_azim + phi, self.current_roll)
+
+    def move_theta_rel(self, theta):
+        self.log.info("Moving Theta by {} degrees".format(theta))
+        self.move_to_azim_roll(self.current_azim, self.current_roll + theta)
+
+    def move_feed_roll(self, roll):
+        self.log.info("Moving feed roll to {} degrees".format(roll))
+        self.chamber_inst.write(f"POS:MOVE:ROLL:FEED {roll}")
+        self.chamber_inst.write("POS:MOVE:INIT")
+        self.wait_for_move_end()
+        self.current_feed_roll = self.chamber_inst.query("POS:MOVE:ROLL:FEED?")
+
+    def reset_phi(self):
+        self.log.info("Resetting Phi.")
+        self.move_to_azim_roll(0, self.current_roll)
+        self.phi = 0
+
+    def reset_theta(self):
+        self.log.info("Resetting Theta.")
+        self.move_to_azim_roll(self.current_azim, 0)
+        self.theta = 0
+
+    def reset_phi_theta(self):
+        """ Resets signal generator."""
+        self.log.info("Resetting to home.")
+        self.chamber_inst.write(f"POS:ZERO:GOTO")
+        self.wait_for_move_end()
+
+    # Keysight-provided functions
+    def wait_for_move_end(self):
+        moving_bitmask = 4
+        while True:
+            stat = int(self.chamber_inst.query("STAT:OPER:COND?"))
+            if (stat & moving_bitmask) == 0:
+                return
+            time.sleep(0.25)
+
+    def wait_for_sweep_end(self):
+        sweeping_bitmask = 16
+        while True:
+            stat = int(self.chamber_inst.query("STAT:OPER:COND?"))
+            if (stat & sweeping_bitmask) == 0:
+                return
+            time.sleep(0.25)
+
+    def move_to_azim_roll(self, azim, roll):
+        self.chamber_inst.write(f"POS:MOVE:AZIM {azim};ROLL {roll}")
+        self.chamber_inst.write("POS:MOVE:INIT")
+        self.wait_for_move_end()
+        curr_motor = self.chamber_inst.query("POS:CURR:MOT?")
+        curr_azim, curr_roll = map(float, (curr_motor.split(',')))
+        self.current_azim = curr_azim
+        self.current_roll = curr_roll
+        return curr_azim, curr_roll
+
+    def sweep_setup(self, azim_sss: tuple, roll_sss: tuple, sweep_type: str):
+        self.chamber_inst.write(
+            f"POS:SWE:AZIM:STAR {azim_sss[0]};STOP {azim_sss[1]};STEP {azim_sss[2]}"
+        )
+        self.chamber_inst.write(
+            f"POS:SWE:ROLL:STAR {roll_sss[0]};STOP {roll_sss[1]};STEP {roll_sss[2]}"
+        )
+        self.chamber_inst.write(f"POS:SWE:TYPE {sweep_type}")
+        self.chamber_inst.write("POS:SWE:CONT 1")
+
+    def sweep_init(self):
+
+        def query_float_list(inst, scpi):
+            resp = inst.query(scpi)
+            return list(map(float, resp.split(',')))
+
+        self.chamber_inst.write("POS:SWE:INIT")
+        self.wait_for_sweep_end()
+        azims = query_float_list(self.chamber_inst, "FETC:AZIM?")
+        rolls = query_float_list(self.chamber_inst, "FETC:ROLL?")
+        phis = query_float_list(self.chamber_inst, "FETC:DUT:PHI?")
+        thetas = query_float_list(self.chamber_inst, "FETC:DUT:THET?")
+        return zip(azims, rolls, phis, thetas)
+
+    def configure_positioner(self, pos_name, pos_visa_addr):
+        select = "True"
+        simulate = "False"
+        options = ""
+        data = f"'{pos_name}~{select}~{simulate}~{pos_visa_addr}~{options}'"
+        self.chamber_inst.write(f"EQU:CONF {data}")
+        self.chamber_inst.write("EQU:UPD")
diff --git a/acts_tests/acts_contrib/test_utils/cellular/mock_chamber.py b/acts_tests/acts_contrib/test_utils/cellular/mock_chamber.py
new file mode 100644
index 0000000..36d74c5
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/cellular/mock_chamber.py
@@ -0,0 +1,65 @@
+# Copyright 2020 Google Inc. All Rights Reserved.
+# Author: oelayach@google.com
+from acts import logger
+from ota_chamber import Chamber
+
+
+class MockChamber(Chamber):
+    """Base class implementation for signal generators.
+
+    Base class provides functions whose implementation is shared by all
+    chambers.
+    """
+
+    def __init__(self, config):
+        self.config = config
+        self.log = logger.create_tagged_trace_logger("{}{}".format(
+            self.config['brand'], self.config['model']))
+        self.id_check(self.config)
+        self.reset()
+
+    def id_check(self, config):
+        """ Check Chamber."""
+        self.log.info("ID Check Successful.")
+
+    def reset(self):
+        """ Resets Chamber."""
+        self.log.info("Resetting instrument.")
+
+    def disconnect(self):
+        """ Disconnects Chamber."""
+        self.log.info("Disconnecting instrument.")
+
+    def get_phi(self):
+        return self.phi
+
+    def get_theta(self):
+        return self.phi
+
+    def move_phi_abs(self, phi):
+        self.log.info("Moving to Phi={}".format(phi))
+        self.phi = phi
+
+    def move_theta_abs(self, theta):
+        self.log.info("Moving to Theta={}".format(theta))
+        self.theta = theta
+
+    def move_phi_rel(self, phi):
+        self.log.info("Moving Phi by {} degrees".format(phi))
+        self.phi = self.phi + phi
+
+    def move_theta_rel(self, theta):
+        self.log.info("Moving Theta by {} degrees".format(theta))
+        self.theta = self.theta + theta
+
+    def reset_phi(self):
+        self.log.info("Resetting Phi.")
+        self.phi = 0
+
+    def reset_theta(self):
+        self.log.info("Resetting Theta.")
+        self.theta = 0
+
+    def reset_phi_theta(self):
+        """ Resets signal generator."""
+        self.log.info("Resetting to home.")
diff --git a/acts_tests/acts_contrib/test_utils/cellular/ota_chamber.py b/acts_tests/acts_contrib/test_utils/cellular/ota_chamber.py
new file mode 100644
index 0000000..923f22b
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/cellular/ota_chamber.py
@@ -0,0 +1,94 @@
+# Copyright 2020 Google Inc. All Rights Reserved.
+# Author: oelayach@google.com
+
+import importlib
+from acts import logger
+
+ACTS_CONTROLLER_CONFIG_NAME = 'Chamber'
+
+
+def create(configs):
+    """Factory method for Signal Generators.
+
+    Args:
+        configs: list of dicts with signal generator settings
+    """
+    SUPPORTED_MODELS = {
+        ("Keysight", "CATR"): "lassen.controllers.chamber.KeysightCATRChamber",
+        ("Mock", "Chamber"): "lassen.controllers.chamber.MockChamber"
+    }
+    objs = []
+    for config in configs:
+        if (config["brand"], config["model"]) not in SUPPORTED_MODELS:
+            raise ValueError("Not a valid chamber model.")
+        module = importlib.import_module(SUPPORTED_MODELS[(config["brand"],
+                                                           config["model"])])
+        chamber = module.Chamber(config)
+        objs.append(chamber)
+    return objs
+
+
+def destroy(devices):
+    for device in devices:
+        device.reset()
+
+
+class Chamber():
+    """Base class implementation for signal generators.
+
+    Base class provides functions whose implementation is shared by all
+    chambers.
+    """
+
+    def __init__(self, config):
+        self.config = config
+        self.log = logger.create_tagged_trace_logger("{}{}".format(
+            self.config['brand'], self.config['model']))
+        self.id_check(self.config)
+        self.reset()
+
+    def id_check(self, config):
+        """ Resets signal generator."""
+        self.log.info("ID Check Successful.")
+
+    def reset(self):
+        """ Resets signal generator."""
+        self.log.info("Resetting instrument.")
+
+    def disconnect(self):
+        """ Disconnects Chamber."""
+        self.log.info("Disconnecting instrument.")
+
+    def get_phi(self):
+        return self.phi
+
+    def get_theta(self):
+        return self.phi
+
+    def move_phi_abs(self, phi):
+        self.log.info("Moving to Phi={}".format(phi))
+        self.phi = phi
+
+    def move_theta_abs(self, theta):
+        self.log.info("Moving to Theta={}".format(theta))
+        self.theta = theta
+
+    def move_phi_rel(self, phi):
+        self.log.info("Moving Phi by {} degrees".format(phi))
+        self.phi = self.phi + phi
+
+    def move_theta_rel(self, theta):
+        self.log.info("Moving Theta by {} degrees".format(theta))
+        self.theta = self.theta + theta
+
+    def reset_phi(self):
+        self.log.info("Resetting Phi.")
+        self.phi = 0
+
+    def reset_theta(self):
+        self.log.info("Resetting Theta.")
+        self.theta = 0
+
+    def reset_phi_theta(self):
+        """ Resets signal generator."""
+        self.log.info("Resetting to home.")
diff --git a/acts_tests/acts_contrib/test_utils/cellular/performance/CellularThroughputBaseTest.py b/acts_tests/acts_contrib/test_utils/cellular/performance/CellularThroughputBaseTest.py
new file mode 100644
index 0000000..57e66ff
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/cellular/performance/CellularThroughputBaseTest.py
@@ -0,0 +1,481 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import csv
+import itertools
+import json
+import re
+
+import numpy
+import os
+import time
+from acts import asserts
+from acts import context
+from acts import base_test
+from acts import utils
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.controllers.utils_lib import ssh
+from acts.controllers import iperf_server as ipf
+from acts_contrib.test_utils.cellular.keysight_5g_testapp import Keysight5GTestApp
+from acts_contrib.test_utils.cellular.performance import cellular_performance_test_utils as cputils
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from functools import partial
+
+LONG_SLEEP = 10
+MEDIUM_SLEEP = 2
+IPERF_TIMEOUT = 10
+SHORT_SLEEP = 1
+SUBFRAME_LENGTH = 0.001
+STOP_COUNTER_LIMIT = 3
+
+
+class CellularThroughputBaseTest(base_test.BaseTestClass):
+    """Base class for Cellular Throughput Testing
+
+    This base class enables cellular throughput tests on a lab/callbox setup
+    with PHY layer or iperf traffic.
+    """
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+        self.testclass_params = None
+
+    def setup_class(self):
+        """Initializes common test hardware and parameters.
+
+        This function initializes hardwares and compiles parameters that are
+        common to all tests in this class.
+        """
+        # Setup controllers
+        self.dut = self.android_devices[-1]
+        self.keysight_test_app = Keysight5GTestApp(
+            self.user_params['Keysight5GTestApp'])
+        self.iperf_server = self.iperf_servers[0]
+        self.iperf_client = self.iperf_clients[0]
+        self.remote_server = ssh.connection.SshConnection(
+            ssh.settings.from_config(
+                self.user_params['RemoteServer']['ssh_config']))
+
+        # Configure Tester
+        if self.testclass_params.get('reload_scpi', 1):
+            self.keysight_test_app.import_scpi_file(
+                self.testclass_params['scpi_file'])
+
+        # Declare testclass variables
+        self.testclass_results = collections.OrderedDict()
+
+        # Configure test retries
+        self.user_params['retry_tests'] = [self.__class__.__name__]
+
+        # Turn Airplane mode on
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+
+    def teardown_class(self):
+        self.log.info('Turning airplane mode on')
+        try:
+            asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                                'Can not turn on airplane mode.')
+        except:
+            self.log.warning('Cannot perform teardown operations on DUT.')
+        try:
+            self.keysight_test_app.set_cell_state('LTE', 1, 0)
+            self.keysight_test_app.destroy()
+        except:
+            self.log.warning('Cannot perform teardown operations on tester.')
+        self.process_testclass_results()
+
+    def setup_test(self):
+        self.retry_flag = False
+        if self.testclass_params['enable_pixel_logs']:
+            cputils.start_pixel_logger(self.dut)
+
+    def teardown_test(self):
+        self.retry_flag = False
+        self.log.info('Turing airplane mode on')
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+        if self.keysight_test_app.get_cell_state('LTE', 'CELL1'):
+            self.log.info('Turning LTE off.')
+            self.keysight_test_app.set_cell_state('LTE', 'CELL1', 0)
+        log_path = os.path.join(
+            context.get_current_context().get_full_output_path(), 'pixel_logs')
+        os.makedirs(self.log_path, exist_ok=True)
+        if self.testclass_params['enable_pixel_logs']:
+            cputils.stop_pixel_logger(self.dut, log_path)
+        self.process_testcase_results()
+        self.pass_fail_check()
+
+    def on_retry(self):
+        """Function to control test logic on retried tests.
+
+        This function is automatically executed on tests that are being
+        retried. In this case the function resets wifi, toggles it off and on
+        and sets a retry_flag to enable further tweaking the test logic on
+        second attempts.
+        """
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+        if self.keysight_test_app.get_cell_state('LTE', 'CELL1'):
+            self.log.info('Turning LTE off.')
+            self.keysight_test_app.set_cell_state('LTE', 'CELL1', 0)
+        self.retry_flag = True
+
+    def pass_fail_check(self):
+        pass
+
+    def process_testcase_results(self):
+        pass
+
+    def process_testclass_results(self):
+        pass
+
+    def get_per_cell_power_sweeps(self, testcase_params):
+        raise NotImplementedError(
+            'get_per_cell_power_sweeps must be implemented.')
+
+    def compile_test_params(self, testcase_params):
+        """Function that completes all test params based on the test name.
+
+        Args:
+            testcase_params: dict containing test-specific parameters
+        """
+        # Measurement Duration
+        testcase_params['bler_measurement_length'] = int(
+            self.testclass_params['traffic_duration'] / SUBFRAME_LENGTH)
+        # Cell power sweep
+        # TODO: Make this a function to support single power and sweep modes for each cell
+        testcase_params['cell_power_sweep'] = self.get_per_cell_power_sweeps(
+            testcase_params)
+        # Traffic & iperf params
+        if self.testclass_params['traffic_type'] == 'PHY':
+            return testcase_params
+        if self.testclass_params['traffic_type'] == 'TCP':
+            testcase_params['iperf_socket_size'] = self.testclass_params.get(
+                'tcp_socket_size', None)
+            testcase_params['iperf_processes'] = self.testclass_params.get(
+                'tcp_processes', 1)
+        elif self.testclass_params['traffic_type'] == 'UDP':
+            testcase_params['iperf_socket_size'] = self.testclass_params.get(
+                'udp_socket_size', None)
+            testcase_params['iperf_processes'] = self.testclass_params.get(
+                'udp_processes', 1)
+        adb_iperf_server = isinstance(self.iperf_server,
+                                      ipf.IPerfServerOverAdb)
+        if testcase_params['traffic_direction'] == 'DL':
+            reverse_direction = 0 if adb_iperf_server else 1
+            testcase_params[
+                'use_client_output'] = False if adb_iperf_server else True
+        elif testcase_params['traffic_direction'] == 'UL':
+            reverse_direction = 1 if adb_iperf_server else 0
+            testcase_params[
+                'use_client_output'] = True if adb_iperf_server else False
+        testcase_params['iperf_args'] = wputils.get_iperf_arg_string(
+            duration=self.testclass_params['traffic_duration'],
+            reverse_direction=reverse_direction,
+            traffic_type=self.testclass_params['traffic_type'],
+            socket_size=testcase_params['iperf_socket_size'],
+            num_processes=testcase_params['iperf_processes'],
+            udp_throughput=self.testclass_params['UDP_rates'].get(
+                testcase_params['num_dl_cells'],
+                self.testclass_params['UDP_rates']["default"]),
+            udp_length=1440)
+        return testcase_params
+
+    def run_iperf_traffic(self, testcase_params):
+        self.iperf_server.start(tag=0)
+        dut_ip = self.dut.droid.connectivityGetIPv4Addresses('rmnet0')[0]
+        if 'iperf_server_address' in self.testclass_params:
+            iperf_server_address = self.testclass_params[
+                'iperf_server_address']
+        elif isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+            iperf_server_address = dut_ip
+        else:
+            iperf_server_address = wputils.get_server_address(
+                self.remote_server, dut_ip, '255.255.255.0')
+        client_output_path = self.iperf_client.start(
+            iperf_server_address, testcase_params['iperf_args'], 0,
+            self.testclass_params['traffic_duration'] + IPERF_TIMEOUT)
+        server_output_path = self.iperf_server.stop()
+        # Parse and log result
+        if testcase_params['use_client_output']:
+            iperf_file = client_output_path
+        else:
+            iperf_file = server_output_path
+        try:
+            iperf_result = ipf.IPerfResult(iperf_file)
+            current_throughput = numpy.mean(iperf_result.instantaneous_rates[
+                self.testclass_params['iperf_ignored_interval']:-1]) * 8 * (
+                    1.024**2)
+        except:
+            self.log.warning(
+                'ValueError: Cannot get iperf result. Setting to 0')
+            current_throughput = 0
+        return current_throughput
+
+    def run_single_throughput_measurement(self, testcase_params):
+        result = collections.OrderedDict()
+        self.log.info('Starting BLER & throughput tests.')
+        if testcase_params['endc_combo_config']['nr_cell_count']:
+            self.keysight_test_app.start_bler_measurement(
+                'NR5G', testcase_params['endc_combo_config']['nr_dl_carriers'],
+                testcase_params['bler_measurement_length'])
+        if testcase_params['endc_combo_config']['lte_cell_count']:
+            self.keysight_test_app.start_bler_measurement(
+                'LTE', testcase_params['endc_combo_config']['lte_carriers'][0],
+                testcase_params['bler_measurement_length'])
+
+        if self.testclass_params['traffic_type'] != 'PHY':
+            result['iperf_throughput'] = self.run_iperf_traffic(
+                testcase_params)
+
+        if testcase_params['endc_combo_config']['nr_cell_count']:
+            result['nr_bler_result'] = self.keysight_test_app.get_bler_result(
+                'NR5G', testcase_params['endc_combo_config']['nr_dl_carriers'],
+                testcase_params['bler_measurement_length'])
+            result['nr_tput_result'] = self.keysight_test_app.get_throughput(
+                'NR5G', testcase_params['endc_combo_config']['nr_dl_carriers'])
+        if testcase_params['endc_combo_config']['lte_cell_count']:
+            result['lte_bler_result'] = self.keysight_test_app.get_bler_result(
+                'LTE', testcase_params['endc_combo_config']['lte_carriers'],
+                testcase_params['bler_measurement_length'])
+            result['lte_tput_result'] = self.keysight_test_app.get_throughput(
+                'LTE', testcase_params['endc_combo_config']['lte_carriers'])
+        return result
+
+    def print_throughput_result(self, result):
+        # Print Test Summary
+        if 'nr_tput_result' in result:
+            self.log.info(
+                "----NR5G STATS-------NR5G STATS-------NR5G STATS---")
+            self.log.info(
+                "DL PHY Tput (Mbps):\tMin: {:.2f},\tAvg: {:.2f},\tMax: {:.2f},\tTheoretical: {:.2f}"
+                .format(
+                    result['nr_tput_result']['total']['DL']['min_tput'],
+                    result['nr_tput_result']['total']['DL']['average_tput'],
+                    result['nr_tput_result']['total']['DL']['max_tput'],
+                    result['nr_tput_result']['total']['DL']
+                    ['theoretical_tput']))
+            self.log.info(
+                "UL PHY Tput (Mbps):\tMin: {:.2f},\tAvg: {:.2f},\tMax: {:.2f},\tTheoretical: {:.2f}"
+                .format(
+                    result['nr_tput_result']['total']['UL']['min_tput'],
+                    result['nr_tput_result']['total']['UL']['average_tput'],
+                    result['nr_tput_result']['total']['UL']['max_tput'],
+                    result['nr_tput_result']['total']['UL']
+                    ['theoretical_tput']))
+            self.log.info("DL BLER: {:.2f}%\tUL BLER: {:.2f}%".format(
+                result['nr_bler_result']['total']['DL']['nack_ratio'] * 100,
+                result['nr_bler_result']['total']['UL']['nack_ratio'] * 100))
+        if 'lte_tput_result' in result:
+            self.log.info("----LTE STATS-------LTE STATS-------LTE STATS---")
+            self.log.info(
+                "DL PHY Tput (Mbps):\tMin: {:.2f},\tAvg: {:.2f},\tMax: {:.2f},\tTheoretical: {:.2f}"
+                .format(
+                    result['lte_tput_result']['total']['DL']['min_tput'],
+                    result['lte_tput_result']['total']['DL']['average_tput'],
+                    result['lte_tput_result']['total']['DL']['max_tput'],
+                    result['lte_tput_result']['total']['DL']
+                    ['theoretical_tput']))
+            if self.testclass_params['lte_ul_mac_padding']:
+                self.log.info(
+                    "UL PHY Tput (Mbps):\tMin: {:.2f},\tAvg: {:.2f},\tMax: {:.2f},\tTheoretical: {:.2f}"
+                    .format(
+                        result['lte_tput_result']['total']['UL']['min_tput'],
+                        result['lte_tput_result']['total']['UL']
+                        ['average_tput'],
+                        result['lte_tput_result']['total']['UL']['max_tput'],
+                        result['lte_tput_result']['total']['UL']
+                        ['theoretical_tput']))
+            self.log.info("DL BLER: {:.2f}%\tUL BLER: {:.2f}%".format(
+                result['lte_bler_result']['total']['DL']['nack_ratio'] * 100,
+                result['lte_bler_result']['total']['UL']['nack_ratio'] * 100))
+            if self.testclass_params['traffic_type'] != 'PHY':
+                self.log.info("{} Tput: {:.2f} Mbps".format(
+                    self.testclass_params['traffic_type'],
+                    result['iperf_throughput']))
+
+    def setup_tester(self, testcase_params):
+        # Configure all cells
+        for cell_idx, cell in enumerate(
+                testcase_params['endc_combo_config']['cell_list']):
+            self.keysight_test_app.set_cell_duplex_mode(
+                cell['cell_type'], cell['cell_number'], cell['duplex_mode'])
+            self.keysight_test_app.set_cell_band(cell['cell_type'],
+                                                 cell['cell_number'],
+                                                 cell['band'])
+            self.keysight_test_app.set_cell_dl_power(
+                cell['cell_type'], cell['cell_number'],
+                testcase_params['cell_power_sweep'][cell_idx][0], 1)
+            if cell['cell_type'] == 'NR5G':
+                self.keysight_test_app.set_nr_subcarrier_spacing(
+                    cell['cell_number'], cell['subcarrier_spacing'])
+            if 'channel' in cell:
+                self.keysight_test_app.set_cell_channel(
+                    cell['cell_type'], cell['cell_number'], cell['channel'])
+            self.keysight_test_app.set_cell_bandwidth(cell['cell_type'],
+                                                      cell['cell_number'],
+                                                      cell['dl_bandwidth'])
+            self.keysight_test_app.set_cell_mimo_config(
+                cell['cell_type'], cell['cell_number'], 'DL',
+                cell['dl_mimo_config'])
+            if cell['cell_type'] == 'LTE':
+                self.keysight_test_app.set_lte_cell_transmission_mode(
+                    cell['cell_number'], cell['transmission_mode'])
+                self.keysight_test_app.set_lte_control_region_size(
+                    cell['cell_number'], 1)
+            if cell['ul_enabled'] and cell['cell_type'] == 'NR5G':
+                self.keysight_test_app.set_cell_mimo_config(
+                    cell['cell_type'], cell['cell_number'], 'UL',
+                    cell['ul_mimo_config'])
+
+        if testcase_params.get('force_contiguous_nr_channel', False):
+            self.keysight_test_app.toggle_contiguous_nr_channels(1)
+
+        if testcase_params['endc_combo_config']['lte_cell_count']:
+            self.keysight_test_app.set_lte_cell_mcs(
+                'CELL1', testcase_params['lte_dl_mcs_table'],
+                testcase_params['lte_dl_mcs'],
+                testcase_params['lte_ul_mcs_table'],
+                testcase_params['lte_ul_mcs'])
+            self.keysight_test_app.set_lte_ul_mac_padding(
+                self.testclass_params['lte_ul_mac_padding'])
+
+        if testcase_params['endc_combo_config']['nr_cell_count']:
+            if 'schedule_scenario' in testcase_params:
+                self.keysight_test_app.set_nr_cell_schedule_scenario(
+                    'CELL1',
+                    testcase_params['schedule_scenario'])
+            self.keysight_test_app.set_nr_ul_dft_precoding(
+                'CELL1', testcase_params['transform_precoding'])
+            self.keysight_test_app.set_nr_cell_mcs(
+                'CELL1', testcase_params['nr_dl_mcs'],
+                testcase_params['nr_ul_mcs'])
+            self.keysight_test_app.set_dl_carriers(
+                testcase_params['endc_combo_config']['nr_dl_carriers'])
+            self.keysight_test_app.set_ul_carriers(
+                testcase_params['endc_combo_config']['nr_ul_carriers'])
+
+        # Turn on LTE cells
+        for cell in testcase_params['endc_combo_config']['cell_list']:
+            if cell['cell_type'] == 'LTE' and not self.keysight_test_app.get_cell_state(
+                    cell['cell_type'], cell['cell_number']):
+                self.log.info('Turning LTE Cell {} on.'.format(
+                    cell['cell_number']))
+                self.keysight_test_app.set_cell_state(cell['cell_type'],
+                                                      cell['cell_number'], 1)
+
+        # Activate LTE aggregation
+        if testcase_params['endc_combo_config']['lte_scc_list']:
+            self.keysight_test_app.apply_lte_carrier_agg(
+                testcase_params['endc_combo_config']['lte_scc_list'])
+
+        self.log.info('Waiting for LTE connections')
+        # Turn airplane mode off
+        num_apm_toggles = 5
+        for idx in range(num_apm_toggles):
+            self.log.info('Turning off airplane mode')
+            asserts.assert_true(utils.force_airplane_mode(self.dut, False),
+                                'Can not turn off airplane mode.')
+            if self.keysight_test_app.wait_for_cell_status(
+                    'LTE', 'CELL1', 'CONN', 180):
+                break
+            elif idx < num_apm_toggles - 1:
+                self.log.info('Turning on airplane mode')
+                asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                                    'Can not turn on airplane mode.')
+                time.sleep(MEDIUM_SLEEP)
+            else:
+                asserts.fail('DUT did not connect to LTE.')
+
+        if testcase_params['endc_combo_config']['nr_cell_count']:
+            self.keysight_test_app.apply_carrier_agg()
+            self.log.info('Waiting for 5G connection')
+            connected = self.keysight_test_app.wait_for_cell_status(
+                'NR5G', testcase_params['endc_combo_config']['nr_cell_count'],
+                ['ACT', 'CONN'], 60)
+            if not connected:
+                asserts.fail('DUT did not connect to NR.')
+        time.sleep(SHORT_SLEEP)
+
+    def _test_throughput_bler(self, testcase_params):
+        """Test function to run cellular throughput and BLER measurements.
+
+        The function runs BLER/throughput measurement after configuring the
+        callbox and DUT. The test supports running PHY or TCP/UDP layer traffic
+        in a variety of band/carrier/mcs/etc configurations.
+
+        Args:
+            testcase_params: dict containing test-specific parameters
+        Returns:
+            result: dict containing throughput results and meta data
+        """
+        # Prepare results dicts
+        testcase_params = self.compile_test_params(testcase_params)
+        testcase_results = collections.OrderedDict()
+        testcase_results['testcase_params'] = testcase_params
+        testcase_results['results'] = []
+
+        # Setup tester and wait for DUT to connect
+        self.setup_tester(testcase_params)
+
+        # Run throughput test loop
+        stop_counter = 0
+        if testcase_params['endc_combo_config']['nr_cell_count']:
+            self.keysight_test_app.select_display_tab('NR5G', 1, 'BTHR',
+                                                      'OTAGRAPH')
+        else:
+            self.keysight_test_app.select_display_tab('LTE', 1, 'BTHR',
+                                                      'OTAGRAPH')
+        for power_idx in range(len(testcase_params['cell_power_sweep'][0])):
+            result = collections.OrderedDict()
+            # Set DL cell power
+            result['cell_power'] = []
+            for cell_idx, cell in enumerate(
+                    testcase_params['endc_combo_config']['cell_list']):
+                current_cell_power = testcase_params['cell_power_sweep'][
+                    cell_idx][power_idx]
+                result['cell_power'].append(current_cell_power)
+                self.keysight_test_app.set_cell_dl_power(
+                    cell['cell_type'], cell['cell_number'], current_cell_power,
+                    1)
+
+            # Start BLER and throughput measurements
+            result = self.run_single_throughput_measurement(testcase_params)
+            testcase_results['results'].append(result)
+
+            self.print_throughput_result(result)
+
+            if (('lte_bler_result' in result
+                 and result['lte_bler_result']['total']['DL']['nack_ratio'] *
+                 100 > 99) or
+                ('nr_bler_result' in result
+                 and result['nr_bler_result']['total']['DL']['nack_ratio'] *
+                 100 > 99)):
+                stop_counter = stop_counter + 1
+            else:
+                stop_counter = 0
+            if stop_counter == STOP_COUNTER_LIMIT:
+                break
+
+        # Save results
+        self.testclass_results[self.current_test_name] = testcase_results
diff --git a/acts_tests/acts_contrib/test_utils/cellular/performance/__init__.py b/acts_tests/acts_contrib/test_utils/cellular/performance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/cellular/performance/__init__.py
diff --git a/acts_tests/acts_contrib/test_utils/cellular/performance/cellular_performance_test_utils.py b/acts_tests/acts_contrib/test_utils/cellular/performance/cellular_performance_test_utils.py
new file mode 100644
index 0000000..4aaff91
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/cellular/performance/cellular_performance_test_utils.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import logging
+import os
+import time
+
+PCC_PRESET_MAPPING = {
+    'N257': {
+        'low': 2054999,
+        'mid': 2079165,
+        'high': 2090832
+    },
+    'N258': {
+        'low': 2017499,
+        'mid': 2043749,
+        'high': 2057499
+    },
+    'N260': {
+        'low': 2229999,
+        'mid': 2254165,
+        'high': 2265832
+    },
+    'N261': {
+        'low': 2071667
+    }
+}
+
+DUPLEX_MODE_TO_BAND_MAPPING = {
+    'LTE': {
+        'FDD': [
+            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21,
+            22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 65, 66, 67, 68, 69, 70,
+            71, 72, 73, 74, 75, 76, 85, 252, 255
+        ],
+        'TDD': [
+            33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46, 47, 48,
+            50, 51, 53
+        ]
+    },
+    'NR5G': {
+        'FDD': [
+            'N1', 'N2', 'N3', 'N5', 'N7', 'N8', 'N12', 'N13', 'N14', 'N18',
+            'N20', 'N25', 'N26', 'N28', 'N30', 'N65', 'N66', 'N70', 'N71',
+            'N74'
+        ],
+        'TDD': [
+            'N34', 'N38', 'N39', 'N40', 'N41', 'N48', 'N50', 'N51', 'N53',
+            'N77', 'N78', 'N79', 'N90', 'N257', 'N258', 'N259', 'N260', 'N261'
+        ]
+    },
+}
+
+
+def extract_test_id(testcase_params, id_fields):
+    test_id = collections.OrderedDict(
+        (param, testcase_params[param]) for param in id_fields)
+    return test_id
+
+
+def start_pixel_logger(ad):
+    """Function to start pixel logger with default log mask.
+
+    Args:
+        ad: android device on which to start logger
+    """
+
+    try:
+        ad.adb.shell(
+            'rm -R /storage/emulated/0/Android/data/com.android.pixellogger/files/logs/logs/'
+        )
+    except:
+        pass
+    ad.adb.shell(
+        'am startservice -a com.android.pixellogger.service.logging.LoggingService.ACTION_START_LOGGING'
+    )
+
+
+def stop_pixel_logger(ad, log_path, tag=None):
+    """Function to stop pixel logger and retrieve logs
+
+    Args:
+        ad: android device on which to start logger
+        log_path: location of saved logs
+    """
+    ad.adb.shell(
+        'am startservice -a com.android.pixellogger.service.logging.LoggingService.ACTION_STOP_LOGGING'
+    )
+    logging.info('Waiting for Pixel log file')
+    file_name = None
+    file_size = 0
+    previous_file_size = 0
+    for idx in range(600):
+        try:
+            file = ad.adb.shell(
+                'ls -l /storage/emulated/0/Android/data/com.android.pixellogger/files/logs/logs/'
+            ).split(' ')
+            file_name = file[-1]
+            file_size = file[-4]
+        except:
+            file_name = None
+            file_size = 0
+        if file_name and file_size == previous_file_size:
+            logging.info('Log file found after {}s.'.format(idx))
+            break
+        else:
+            previous_file_size = file_size
+            time.sleep(1)
+    try:
+        local_file_name = '{}_{}'.format(file_name, tag) if tag else file_name
+        local_path = os.path.join(log_path, local_file_name)
+        ad.pull_files(
+            '/storage/emulated/0/Android/data/com.android.pixellogger/files/logs/logs/{}'
+            .format(file_name), log_path)
+        return local_path
+    except:
+        logging.error('Could not pull pixel logs.')
+
+
+def log_system_power_metrics(ad, verbose=1):
+    # Log temperature sensors
+    if verbose:
+        temp_sensors = ad.adb.shell(
+            'ls -1 /dev/thermal/tz-by-name/').splitlines()
+    else:
+        temp_sensors = ['BIG', 'battery', 'quiet_therm', 'usb_pwr_therm']
+    temp_measurements = collections.OrderedDict()
+    for sensor in temp_sensors:
+        try:
+            temp_measurements[sensor] = ad.adb.shell(
+                'cat /dev/thermal/tz-by-name/{}/temp'.format(sensor))
+        except:
+            temp_measurements[sensor] = float('nan')
+    logging.debug('Temperature sensor readings: {}'.format(temp_measurements))
+
+    # Log mitigation items
+    if verbose:
+        mitigation_points = [
+            "batoilo",
+            "ocp_cpu1",
+            "ocp_cpu2",
+            "ocp_gpu",
+            "ocp_tpu",
+            "smpl_warn",
+            "soft_ocp_cpu1",
+            "soft_ocp_cpu2",
+            "soft_ocp_gpu",
+            "soft_ocp_tpu",
+            "vdroop1",
+            "vdroop2",
+        ]
+    else:
+        mitigation_points = [
+            "batoilo",
+            "smpl_warn",
+            "vdroop1",
+            "vdroop2",
+        ]
+
+    parameters_f = ['count', 'capacity', 'timestamp', 'voltage']
+    parameters_v = ['count', 'cap', 'time', 'volt']
+    mitigation_measurements = collections.OrderedDict()
+    for mp in mitigation_points:
+        mitigation_measurements[mp] = collections.OrderedDict()
+        for par_f, par_v in zip(parameters_f, parameters_v):
+            mitigation_measurements[mp][par_v] = ad.adb.shell(
+                'cat /sys/devices/virtual/pmic/mitigation/last_triggered_{}/{}_{}'
+                .format(par_f, mp, par_v))
+    logging.debug('Mitigation readings: {}'.format(mitigation_measurements))
+
+    # Log power meter items
+    power_meter_measurements = collections.OrderedDict()
+    for device in ['device0', 'device1']:
+        power_str = ad.adb.shell(
+            'cat /sys/bus/iio/devices/iio:{}/lpf_power'.format(
+                device)).splitlines()
+        power_meter_measurements[device] = collections.OrderedDict()
+        for line in power_str:
+            if line.startswith('CH'):
+                try:
+                    line_split = line.split(', ')
+                    power_meter_measurements[device][line_split[0]] = int(
+                        line_split[1])
+                except (IndexError, ValueError):
+                    continue
+            elif line.startswith('t='):
+                try:
+                    power_meter_measurements[device]['t_pmeter'] = int(
+                        line[2:])
+                except (IndexError, ValueError):
+                    continue
+            else:
+                continue
+        logging.debug(
+            'Power Meter readings: {}'.format(power_meter_measurements))
+
+        # Log battery items
+        if verbose:
+            battery_parameters = [
+                "act_impedance", "capacity", "charge_counter", "charge_full",
+                "charge_full_design", "current_avg", "current_now",
+                "cycle_count", "health", "offmode_charger", "present",
+                "rc_switch_enable", "resistance", "status", "temp",
+                "voltage_avg", "voltage_now", "voltage_ocv"
+            ]
+        else:
+            battery_parameters = [
+                "capacity", "current_avg", "current_now", "voltage_avg",
+                "voltage_now", "voltage_ocv"
+            ]
+
+        battery_meaurements = collections.OrderedDict()
+        for par in battery_parameters:
+            battery_meaurements['bat_{}'.format(par)] = ad.adb.shell(
+                'cat /sys/class/power_supply/maxfg/{}'.format(par))
+        logging.debug('Battery readings: {}'.format(battery_meaurements))
diff --git a/acts_tests/acts_contrib/test_utils/cellular/performance/shannon_log_parser.py b/acts_tests/acts_contrib/test_utils/cellular/performance/shannon_log_parser.py
new file mode 100644
index 0000000..89c2adf
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/cellular/performance/shannon_log_parser.py
@@ -0,0 +1,808 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import datetime
+import gzip
+import itertools
+import logging
+import numpy
+import os
+import re
+import shutil
+import subprocess
+import zipfile
+from acts import context
+from pathlib import Path
+
+_DIGITS_REGEX = re.compile(r'-?\d+')
+_TX_PWR_MAX = 100
+_TX_PWR_MIN = -100
+
+
+class LastNrPower:
+    last_time = 0
+    last_pwr = 0
+
+
+class LastLteValue:
+    last_time = 0
+    last_pwr = 0
+
+
+def _string_to_float(input_string):
+    """Convert string to float value."""
+    try:
+        tmp = float(input_string)
+    except ValueError:
+        print(input_string)
+        tmp = float('nan')
+    return tmp
+
+
+def to_time(time_str):
+    """Convert time string to data time."""
+    return datetime.datetime.strptime(time_str, '%H:%M:%S.%f')
+
+
+def to_time_sec(time_str, start_time):
+    """"Converts time string to seconds elapsed since start time."""
+    return (to_time(time_str) - start_time).total_seconds()
+
+
+class LogParser(object):
+    """Base class to parse log csv files."""
+
+    def __init__(self):
+        self.timestamp_col = -1
+        self.message_col = -1
+        self.start_time = None
+
+        # Dictionary of {log_item_header: log_time_parser} elements
+        self.PARSER_INFO = {
+            r'###[AS] RSRP[': self._parse_lte_rsrp,
+            r'|CC0| PCell RSRP ': self._parse_lte_rsrp2,
+            r'|CC0 Mx0 Sc0| PCell RSRP ': self._parse_lte_rsrp2,
+            r'LT12 PUSCH_Power': self._parse_lte_power,
+            r'UL_PWR(PUSCH)=>pwr_val:': self._parse_lte_power,
+            r'[BM]SSB_RSRP(1)': self._parse_nr_rsrp,
+            r'[BM]SSB_RSRP(0)': self._parse_nr_rsrp,
+            r'###[AS] CurAnt': self._parse_nr_rsrp2,
+            r'[PHY] RF module(': self._parse_fr2_rsrp,
+            #r'[RF] PD : CC0 (monitoring) target_pwr': _parse_fr2_power,
+            #r'[NrPhyTxScheduler][PuschCalcPower] Po_nominal_pusch': _parse_nr_power,
+            r'[NrPhyTxPuschScheduler][PuschCalcPower] Po_nominal_pusch':
+            self._parse_fr2_power,
+            r'[RF NR SUB6] PD : CC0 (monitoring) target_pwr':
+            self._parse_nr_power2,
+            r'[RF NR SUB6] PD : CC1 (monitoring) target_pwr':
+            self._parse_nr_power2,
+            r'[AS] RFAPI_ChangeAntennaSwitch': self._parse_lte_ant_sel,
+            r'[AS] Ant switching': self._parse_lte_ant_sel2,
+            r'###[AS] Select Antenna': self._parse_nr_ant_sel,
+            r'###[AS] CurAnt(': self._parse_nr_ant_sel2,
+            r'[SAR][RESTORE]': self._parse_sar_mode,
+            r'[SAR][NORMAL]': self._parse_sar_mode,
+            r'[SAR][LIMITED-TAPC]': self._parse_tapc_sar_mode,
+            r'###[TAS] [0] ProcessRestoreStage:: [RESTORE]':
+            self._parse_nr_sar_mode,
+            r'###[TAS] [0] ProcessNormalStage:: [NORMAL]':
+            self._parse_nr_sar_mode,
+            r'|CC0| UL Power : PRACH ': self._parse_lte_avg_power,
+            r'activeStackId=0\, [Monitor] txPower ': self._parse_wcdma_power,
+            r'[SAR][DYNAMIC] EN-DC(2) UsedAvgSarLTE': self._parse_sar_values,
+            #r'[SAR][DYNAMIC] UsedAvgSarLTE_100s': self._parse_sar_values2,
+            r'[SAR][DYNAMIC] EN-DC(0) UsedAvgSarLTE':
+            self._parse_lte_sar_values,
+            r'###[TAS] [0] CalcGain:: TotalUsedSar': self._parse_nr_sar_values,
+            r'[SAR][DYNAMIC] IsLTEOn(1) IsNROn(0) ':
+            self._parse_lte_sar_values,
+            r'[SAR][DYNAMIC] IsLTEOn(1) IsNROn(1) ': self._parse_sar_values,
+            r'[MAIN][VolteStatusInd] Volte status ': self._parse_volte_status,
+            r'[PHY] CC0 SLP : dlGrantRatio(3)/ulGrantRatio(3)/RbRatio(3)':
+            self._parse_df_value,
+            r'CC0 AVG: CELLGROUP(0) DCI(D/U):': self._parse_df_value,
+            r'[OL-AIT] band': self._parse_ul_mimo,
+        }
+
+        self.SAR_MODES = [
+            'none', 'MIN', 'SAV_1', 'SAV_2', 'MAIN', 'PRE_SAV', 'LIMITED-TAPC',
+            'MAX', 'none'
+        ]
+        self.SAR_MODES_DESC = [
+            '', 'Minimum', 'Slope Saving1', 'Slope Saving2', 'Maintenance',
+            'Pre-Save', 'Limited TAPC', 'Maximum', ''
+        ]
+
+    def parse_header(self, header_line):
+        header = header_line.split(',')
+        try:
+            self.timestamp_col = header.index('PC Time')
+            self.message_col = header.index('Message')
+        except ValueError:
+            print('Error: PC Time and Message fields are not present')
+        try:
+            self.core_id_col = header.index('Core ID')
+        except:
+            self.core_id_col = self.timestamp_col
+
+    def parse_log(self, log_file, gap_options=0):
+        """Extract required data from the exported CSV file."""
+
+        log_data = LogData()
+        log_data.gap_options = gap_options
+        # Fr-1 as default
+        fr_id = 0
+
+        with open(log_file, 'r') as file:
+            # Read header line
+            header = file.readline()
+            try:
+                self.parse_header(header)
+            except:
+                print('Could not parse header')
+                return log_data
+
+            # Use first message for start time
+            line = file.readline()
+            print(line)
+            line_data = line[1:-2].split('","')
+            if len(line_data) < self.message_col:
+                print('Error: Empty exported file')
+                return log_data
+
+            start_time = to_time(line_data[self.timestamp_col])
+
+            print('Parsing log file ... ', end='', flush=True)
+            for line in file:
+                line_data = line[1:-2].split('","')
+                if len(line_data) < self.message_col + 1:
+                    continue
+
+                message = line_data[self.message_col]
+                if "frIdx 1 " in message:
+                    fr_id = 1
+                elif "frIdx 0 " in message:
+                    fr_id = 0
+                for line_prefix, line_parser in self.PARSER_INFO.items():
+                    if message.startswith(line_prefix):
+                        timestamp = to_time_sec(line_data[self.timestamp_col],
+                                                start_time)
+                        if self.core_id_col == self.timestamp_col:
+                            line_parser(timestamp, message[len(line_prefix):],
+                                        'L1', log_data, fr_id)
+                        else:
+                            if " CC1 " in message:
+                                line_parser(timestamp,
+                                            message[len(line_prefix):], 'L2',
+                                            log_data, fr_id)
+                            else:
+                                line_parser(timestamp,
+                                            message[len(line_prefix):],
+                                            line_data[self.core_id_col],
+                                            log_data, fr_id)
+                        break
+
+            if log_data.nr.tx_pwr_time:
+                if log_data.nr.tx_pwr_time[1] > log_data.nr.tx_pwr_time[0] + 50:
+                    log_data.nr.tx_pwr_time = log_data.nr.tx_pwr_time[1:]
+                    log_data.nr.tx_pwr = log_data.nr.tx_pwr[1:]
+
+            self._find_cur_ant(log_data.lte)
+            self._find_cur_ant(log_data.nr)
+        return log_data
+
+    def get_file_start_time(self, log_file):
+        # Fr-1 as default
+
+        with open(log_file, 'r') as file:
+            # Read header line
+            header = file.readline()
+            try:
+                self.parse_header(header)
+            except:
+                print('Could not parse header')
+                return None
+
+            # Use first message for start time
+            line = file.readline()
+            line_data = line[1:-2].split('","')
+            if len(line_data) < self.message_col:
+                print('Error: Empty exported file')
+                return None
+
+            start_time = to_time(line_data[self.timestamp_col])
+            return start_time
+
+    def set_start_time(self, line):
+        """Set start time of logs to the time in the line."""
+        if len(line) == 0:
+            print("Empty Line")
+            return
+        line_data = line[1:-2].split('","')
+        self.start_time = to_time(line_data[self.timestamp_col])
+
+    def get_message(self, line):
+        """Returns message and timestamp for the line."""
+        line_data = line[1:-2].split('","')
+        if len(line_data) < self.message_col + 1:
+            return None
+
+        self.line_data = line_data
+        return line_data[self.message_col]
+
+    def get_time(self, line):
+        """Convert time string to time in seconds from the start time."""
+        line_data = line[1:-2].split('","')
+        if len(line_data) < self.timestamp_col + 1:
+            return 0
+
+        return to_time_sec(line_data[self.timestamp_col], self.start_time)
+
+    def _feed_nr_power(self, timestamp, tx_pwr, option, lte_nr, window,
+                       default, interval):
+        if option < 101 and LastNrPower.last_time > 0 and timestamp - LastNrPower.last_time > interval:
+            #print ('window=',window, ' interval=',interval, ' gap=',timestamp-LastNrPower.last_time)
+            ti = LastNrPower.last_time
+            while ti < timestamp:
+                ti += (timestamp - LastNrPower.last_time) / window
+                lte_nr.tx_pwr_time.append(ti)
+                if option == 0:
+                    lte_nr.tx_pwr.append(tx_pwr / default)
+                elif option == 1:
+                    lte_nr.tx_pwr.append(LastNrPower.last_pwr)
+                elif option == 2:
+                    lte_nr.tx_pwr.append((tx_pwr + LastNrPower.last_pwr) / 2)
+                elif option == 3:
+                    lte_nr.tx_pwr.append(0)
+                else:
+                    lte_nr.tx_pwr.append(option)
+        else:
+            lte_nr.tx_pwr_time.append(timestamp)
+            lte_nr.tx_pwr.append(tx_pwr)
+        LastNrPower.last_time = timestamp
+        LastNrPower.last_pwr = tx_pwr
+
+    def _feed_lte_power(self, timestamp, tx_pwr, log_data, lte_nr, window,
+                        default, interval):
+        if log_data.gap_options <= 100 and LastLteValue.last_time > 0 and timestamp - LastLteValue.last_time > interval:
+            #print ('window=',window, ' interval=',interval, ' gap=',timestamp-LastLteValue.last_time)
+            ti = LastLteValue.last_time
+            while ti < timestamp:
+                ti += (timestamp - LastLteValue.last_time) / window
+                lte_nr.tx_pwr_time.append(ti)
+                if log_data.gap_options == 0:
+                    lte_nr.tx_pwr.append(tx_pwr / default)
+                elif log_data.gap_options == 1:
+                    lte_nr.tx_pwr.append(LastLteValue.last_pwr)
+                elif log_data.gap_options == 2:
+                    lte_nr.tx_pwr.append((tx_pwr + LastLteValue.last_pwr) / 2)
+                elif log_data.gap_options == 3:
+                    lte_nr.tx_pwr.append(0)
+                else:
+                    lte_nr.tx_pwr.append(log_data.gap_options)
+        else:
+            lte_nr.tx_pwr_time.append(timestamp)
+            lte_nr.tx_pwr.append(tx_pwr)
+        LastLteValue.last_time = timestamp
+        LastLteValue.last_pwr = tx_pwr
+
+    def _parse_lte_power(self, timestamp, message, core_id, log_data, fr_id):
+        match = re.search(r'-?\d+', message)
+        if match:
+            tx_pwr = _string_to_float(match.group())
+            if _TX_PWR_MIN < tx_pwr < _TX_PWR_MAX:
+                self._feed_lte_power(timestamp, tx_pwr, log_data, log_data.lte,
+                                     20, 1, 1)
+
+    def _parse_lte_rsrp(self, timestamp, message, core_id, log_data, fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        rsrp0 = _string_to_float(data[0]) / 100.0
+        rsrp1 = _string_to_float(data[1]) / 100.0
+        if rsrp0 != 0.0 and rsrp1 != 0.0:
+            log_data.lte.rsrp_time.append(timestamp)
+            log_data.lte.rsrp_rx0.append(rsrp0)
+            log_data.lte.rsrp_rx1.append(rsrp1)
+
+    def _parse_lte_rsrp2(self, timestamp, message, core_id, log_data, fr_id):
+        m = re.search('^\[ ?-?\d+ \((.*?)\)', message)
+        if not m:
+            return
+        data = _DIGITS_REGEX.findall(m.group(1))
+        if len(data) < 2:
+            return
+        rsrp0 = _string_to_float(data[0])
+        rsrp1 = _string_to_float(data[1])
+        if rsrp0 != 0.0 and rsrp1 != 0.0:
+            log_data.lte.rsrp2_time.append(timestamp)
+            log_data.lte.rsrp2_rx0.append(rsrp0)
+            log_data.lte.rsrp2_rx1.append(rsrp1)
+
+    def _parse_nr_rsrp(self, timestamp, message, core_id, log_data, fr_id):
+        index = message.find('rx0/rx1/rx2/rx3')
+        if index != -1:
+            data = _DIGITS_REGEX.findall(message[index:])
+            log_data.nr.rsrp_time.append(timestamp)
+            log_data.nr.rsrp_rx0.append(_string_to_float(data[4]) / 100)
+            log_data.nr.rsrp_rx1.append(_string_to_float(data[5]) / 100)
+
+    def _parse_nr_rsrp2(self, timestamp, message, core_id, log_data, fr_id):
+        index = message.find('Rsrp')
+        if index != -1:
+            data = _DIGITS_REGEX.findall(message[index:])
+            log_data.nr.rsrp2_time.append(timestamp)
+            log_data.nr.rsrp2_rx0.append(_string_to_float(data[0]) / 100)
+            log_data.nr.rsrp2_rx1.append(_string_to_float(data[1]) / 100)
+
+    def _parse_fr2_rsrp(self, timestamp, message, core_id, log_data, fr_id):
+        index = message.find('rsrp')
+        data = _DIGITS_REGEX.search(message)
+        module_index = _string_to_float(data.group(0))
+        data = _DIGITS_REGEX.findall(message[index:])
+        rsrp = _string_to_float(data[0])
+
+        if rsrp == 0:
+            return
+        if module_index == 0:
+            log_data.fr2.rsrp0_time.append(timestamp)
+            log_data.fr2.rsrp0.append(rsrp if rsrp < 999 else float('nan'))
+        elif module_index == 1:
+            log_data.fr2.rsrp1_time.append(timestamp)
+            log_data.fr2.rsrp1.append(rsrp if rsrp < 999 else float('nan'))
+
+    def _parse_fr2_power(self, timestamp, message, core_id, log_data, fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        tx_pwr = _string_to_float(data[-1]) / 10
+        if _TX_PWR_MIN < tx_pwr < _TX_PWR_MAX:
+            log_data.fr2.tx_pwr_time.append(timestamp)
+            log_data.fr2.tx_pwr.append(tx_pwr)
+
+    def _parse_nr_power(self, timestamp, message, core_id, log_data, fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        tx_pwr = _string_to_float(data[-1]) / 10
+        if _TX_PWR_MIN < tx_pwr < _TX_PWR_MAX:
+            if core_id == 'L2':
+                self._feed_nr_power(timestamp, tx_pwr, log_data.gap_options,
+                                    log_data.nr2, 5, 1, 1)
+            else:
+                self._feed_nr_power(timestamp, tx_pwr, log_data.gap_options,
+                                    log_data.nr, 5, 1, 1)
+
+    def _parse_nr_power2(self, timestamp, message, core_id, log_data, fr_id):
+        if fr_id != 0:
+            return
+        data = _DIGITS_REGEX.findall(message)
+        tx_pwr = _string_to_float(data[0]) / 10
+        if _TX_PWR_MIN < tx_pwr < _TX_PWR_MAX:
+            if core_id == 'L2':
+                self._feed_nr_power(timestamp, tx_pwr, log_data.gap_options,
+                                    log_data.nr2, 5, 1, 1)
+            else:
+                self._feed_nr_power(timestamp, tx_pwr, log_data.gap_options,
+                                    log_data.nr, 5, 1, 1)
+
+    def _parse_lte_ant_sel(self, timestamp, message, core_id, log_data, fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        new_ant = _string_to_float(data[1])
+        old_ant = _string_to_float(data[2])
+        log_data.lte.ant_sel_time.append(timestamp)
+        log_data.lte.ant_sel_old.append(old_ant)
+        log_data.lte.ant_sel_new.append(new_ant)
+
+    def _parse_lte_ant_sel2(self, timestamp, message, core_id, log_data,
+                            fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        if data[0] == '0':
+            log_data.lte.cur_ant_time.append(timestamp)
+            log_data.lte.cur_ant.append(0)
+        elif data[0] == '1':
+            log_data.lte.cur_ant_time.append(timestamp)
+            log_data.lte.cur_ant.append(1)
+        elif data[0] == '10':
+            log_data.lte.cur_ant_time.append(timestamp)
+            log_data.lte.cur_ant.append(1)
+            log_data.lte.cur_ant_time.append(timestamp)
+            log_data.lte.cur_ant.append(0)
+        elif data[0] == '01':
+            log_data.lte.cur_ant_time.append(timestamp)
+            log_data.lte.cur_ant.append(0)
+            log_data.lte.cur_ant_time.append(timestamp)
+            log_data.lte.cur_ant.append(1)
+
+    def _parse_nr_ant_sel(self, timestamp, message, core_id, log_data, fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        log_data.nr.ant_sel_time.append(timestamp)
+        log_data.nr.ant_sel_new.append(_string_to_float(data[1]))
+        log_data.nr.ant_sel_old.append(_string_to_float(data[0]))
+
+    def _parse_nr_ant_sel2(self, timestamp, message, core_id, log_data, fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        log_data.nr.ant_sel_time.append(timestamp)
+        sel_ant = _string_to_float(data[0])
+        log_data.nr.ant_sel_new.append(sel_ant)
+        if log_data.nr.ant_sel_new:
+            log_data.nr.ant_sel_old.append(log_data.nr.ant_sel_new[-1])
+
+    def _parse_sar_mode(self, timestamp, message, core_id, log_data, fr_id):
+        sar_mode = len(self.SAR_MODES) - 1
+        for i, mode in enumerate(self.SAR_MODES):
+            if message.startswith('[' + mode + ']'):
+                sar_mode = i
+        log_data.lte.sar_mode_time.append(timestamp)
+        log_data.lte.sar_mode.append(sar_mode)
+
+    def _parse_tapc_sar_mode(self, timestamp, message, core_id, log_data,
+                             fr_id):
+        log_data.lte.sar_mode_time.append(timestamp)
+        log_data.lte.sar_mode.append(self.SAR_MODES.index('LIMITED-TAPC'))
+
+    def _parse_nr_sar_mode(self, timestamp, message, core_id, log_data, fr_id):
+        sar_mode = len(self.SAR_MODES) - 1
+        for i, mode in enumerate(self.SAR_MODES):
+            if message.startswith('[' + mode + ']'):
+                sar_mode = i
+
+        log_data.nr.sar_mode_time.append(timestamp)
+        log_data.nr.sar_mode.append(sar_mode)
+
+    def _parse_lte_avg_power(self, timestamp, message, core_id, log_data,
+                             fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        tx_pwr = _string_to_float(data[2])
+        if _TX_PWR_MIN < tx_pwr < _TX_PWR_MAX:
+            log_data.lte.tx_avg_pwr_time.append(timestamp)
+            log_data.lte.tx_avg_pwr.append(tx_pwr)
+
+    def _parse_wcdma_power(self, timestamp, message, core_id, log_data, fr_id):
+        match = re.search(r'-?\d+', message)
+        if match:
+            tx_pwr = _string_to_float(match.group()) / 10
+            if tx_pwr < _TX_PWR_MAX and tx_pwr > _TX_PWR_MIN:
+                log_data.wcdma.tx_pwr_time.append(timestamp)
+                log_data.wcdma.tx_pwr.append(tx_pwr)
+
+    def _parse_sar_values(self, timestamp, message, core_id, log_data, fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        log_data.endc_sar_time.append(timestamp)
+        log_data.endc_sar_lte.append(_string_to_float(data[0]) / 1000.0)
+        log_data.endc_sar_nr.append(_string_to_float(data[1]) / 1000.0)
+
+    def _parse_sar_values2(self, timestamp, message, core_id, log_data, fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        log_data.endc_sar_time.append(timestamp)
+        log_data.endc_sar_lte.append(_string_to_float(data[-3]) / 1000.0)
+        log_data.endc_sar_nr.append(_string_to_float(data[-1]) / 1000.0)
+
+    def _parse_lte_sar_values(self, timestamp, message, core_id, log_data,
+                              fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        log_data.lte_sar_time.append(timestamp)
+        log_data.lte_sar.append(_string_to_float(data[0]) / 1000.0)
+
+    def _parse_nr_sar_values(self, timestamp, message, core_id, log_data,
+                             fr_id):
+        data = _DIGITS_REGEX.findall(message)
+        log_data.nr_sar_time.append(timestamp)
+        log_data.nr_sar.append(_string_to_float(data[0]) / 1000.0)
+
+    def _parse_df_value(self, timestamp, message, core_id, log_data, fr_id):
+        match = re.search(r' \d+', message)
+        if match:
+            nr_df = _string_to_float(match.group())
+            log_data.nr.df = (nr_df / 1000 % 1000) / 100
+            log_data.nr.duty_cycle_time.append(timestamp)
+            log_data.nr.duty_cycle.append(log_data.nr.df * 100)
+        else:
+            match = re.search(r'\d+\\,\d+', message)
+            if match:
+                lte_df = match.group(0).split(",")
+                log_data.lte.df = _string_to_float(lte_df[1]) / 100
+                log_data.lte.duty_cycle_time.append(timestamp)
+                log_data.lte.duty_cycle.append(log_data.nr.df * 100)
+
+    def _parse_volte_status(self, timestamp, message, core_id, log_data,
+                            fr_id):
+        if message.startswith('[0 -> 1]'):
+            log_data.volte_time.append(timestamp)
+            log_data.volte_status.append(1)
+        elif message.startswith('[3 -> 0]'):
+            log_data.volte_time.append(timestamp)
+            log_data.volte_status.append(0)
+
+    def _parse_ul_mimo(self, timestamp, message, core_id, log_data, fr_id):
+        match = re.search(r'UL-MIMO', message)
+        if match:
+            log_data.ul_mimo = 1
+
+    def _find_cur_ant(self, log_data):
+        """Interpolate antenna selection from antenna switching data."""
+        if not log_data.cur_ant_time and log_data.ant_sel_time:
+            if log_data.rsrp_time:
+                start_time = log_data.rsrp_time[0]
+                end_time = log_data.rsrp_time[-1]
+            elif log_data.tx_pwr:
+                start_time = log_data.tx_pwr_time[0]
+                end_time = log_data.tx_pwr_time[-1]
+            else:
+                start_time = log_data.ant_sel_time[0]
+                end_time = log_data.ant_sel_time[-1]
+
+            [sel_time,
+             sel_ant] = self.get_ant_selection(log_data.ant_sel_time,
+                                               log_data.ant_sel_old,
+                                               log_data.ant_sel_new,
+                                               start_time, end_time)
+
+            log_data.cur_ant_time = sel_time
+            log_data.cur_ant = sel_ant
+
+    def get_ant_selection(self, config_time, old_antenna_config,
+                          new_antenna_config, start_time, end_time):
+        """Generate antenna selection data from antenna switching information."""
+        sel_time = []
+        sel_ant = []
+        if not config_time:
+            return [sel_time, sel_ant]
+
+        # Add data point for the start time
+        if config_time[0] > start_time:
+            sel_time = [start_time]
+            sel_ant = [old_antenna_config[0]]
+
+        # Add one data point before the switch and one data point after the switch.
+        for i in range(len(config_time)):
+            if not (i > 0
+                    and old_antenna_config[i - 1] == old_antenna_config[i]
+                    and new_antenna_config[i - 1] == new_antenna_config[i]):
+                sel_time.append(config_time[i])
+                sel_ant.append(old_antenna_config[i])
+            sel_time.append(config_time[i])
+            sel_ant.append(new_antenna_config[i])
+
+        # Add data point for the end time
+        if end_time > config_time[-1]:
+            sel_time.append(end_time)
+            sel_ant.append(new_antenna_config[-1])
+
+        return [sel_time, sel_ant]
+
+
+class RatLogData:
+    """Log data structure for each RAT (LTE/NR)."""
+
+    def __init__(self, label):
+
+        self.label = label
+
+        self.rsrp_time = []  # RSRP time
+        self.rsrp_rx0 = []  # RSRP for receive antenna 0
+        self.rsrp_rx1 = []  # RSRP for receive antenna 1
+
+        # second set of RSRP logs
+        self.rsrp2_time = []  # RSRP time
+        self.rsrp2_rx0 = []  # RSRP for receive antenna 0
+        self.rsrp2_rx1 = []  # RSRP for receive antenna 1
+
+        self.ant_sel_time = []  # Antenna selection/switch time
+        self.ant_sel_old = []  # Previous antenna selection
+        self.ant_sel_new = []  # New antenna selection
+
+        self.cur_ant_time = []  # Antenna selection/switch time
+        self.cur_ant = []  # Previous antenna selection
+
+        self.tx_pwr_time = []  # TX power time
+        self.tx_pwr = []  # TX power
+
+        self.tx_avg_pwr_time = []
+        self.tx_avg_pwr = []
+
+        self.sar_mode = []
+        self.sar_mode_time = []
+
+        self.df = 1.0  # Duty factor for UL transmission
+        self.duty_cycle = []  # Duty factors for UL transmission
+        self.duty_cycle_time = []  # Duty factors for UL transmission
+        self.initial_power = 0
+        self.sar_limit_dbm = None
+        self.avg_window_size = 100
+
+
+class LogData:
+    """Log data structure."""
+
+    def __init__(self):
+        self.lte = RatLogData('LTE')
+        self.lte.avg_window_size = 100
+
+        self.nr = RatLogData('NR CC0')
+        self.nr.avg_window_size = 100
+
+        # NR 2nd CC
+        self.nr2 = RatLogData('NR CC1')
+        self.nr2.avg_window_size = 100
+
+        self.wcdma = RatLogData('WCDMA')
+
+        self.fr2 = RatLogData('FR2')
+        self.fr2.rsrp0_time = []
+        self.fr2.rsrp0 = []
+        self.fr2.rsrp1_time = []
+        self.fr2.rsrp1 = []
+        self.fr2.avg_window_size = 4
+
+        self.lte_sar_time = []
+        self.lte_sar = []
+
+        self.nr_sar_time = []
+        self.nr_sar = []
+
+        self.endc_sar_time = []
+        self.endc_sar_lte = []
+        self.endc_sar_nr = []
+
+        self.volte_time = []
+        self.volte_status = []
+
+        # Options to handle data gaps
+        self.gap_options = 0
+
+        self.ul_mimo = 0  # Is UL_MIMO
+
+
+class ShannonLogger(object):
+
+    def __init__(self, dut=None, modem_bin=None, filter_file_path=None):
+        self.dm_app = shutil.which(r'DMConsole')
+        self.dut = dut
+        if self.dut:
+            self.modem_bin = self.pull_modem_file()
+        elif modem_bin:
+            self.modem_bin = modem_bin
+        else:
+            raise (RuntimeError,
+                   'ShannonLogger requires AndroidDevice or modem binary.')
+        self.filter_file = filter_file_path
+
+    def pull_modem_file(self):
+        local_modem_path = os.path.join(
+            context.get_current_context().get_full_output_path(), 'modem_bin')
+        os.makedirs(local_modem_path, exist_ok=True)
+        try:
+            self.dut.pull_files(
+                '/mnt/vendor/modem_img/images/default/modem.bin',
+                local_modem_path)
+            modem_bin_file = os.path.join(local_modem_path, 'modem.bin')
+        except:
+            self.dut.pull_files(
+                '/mnt/vendor/modem_img/images/default/modem.bin.gz',
+                local_modem_path)
+            modem_zip_file = os.path.join(local_modem_path, 'modem.bin.gz')
+            modem_bin_file = modem_zip_file[:-3]
+            with open(modem_zip_file, 'rb') as in_file:
+                with open(modem_bin_file, 'wb') as out_file:
+                    file_content = gzip.decompress(in_file.read())
+                    out_file.write(file_content)
+        return modem_bin_file
+
+    def _unzip_log(self, log_zip_file, in_place=1):
+        log_zip_file = Path(log_zip_file)
+        with zipfile.ZipFile(log_zip_file, 'r') as zip_ref:
+            file_names = zip_ref.namelist()
+            if in_place:
+                zip_dir = log_zip_file.parent
+            else:
+                zip_dir = log_zip_file.with_suffix('')
+            zip_ref.extractall(zip_dir)
+        unzipped_files = [
+            os.path.join(zip_dir, file_name) for file_name in file_names
+        ]
+        return unzipped_files
+
+    def unzip_modem_logs(self, log_zip_file):
+        log_files = self._unzip_log(log_zip_file, in_place=0)
+        sdm_files = []
+        for log_file in log_files:
+            if zipfile.is_zipfile(log_file):
+                sdm_files.append(self._unzip_log(log_file, in_place=1)[0])
+                os.remove(log_file)
+            elif Path(
+                    log_file
+            ).suffix == '.sdm' and 'sbuff_power_on_log.sdm' not in log_file:
+                sdm_files.append(log_file)
+        return sorted(set(sdm_files))
+
+    def _export_single_log(self, file):
+        temp_file = str(Path(file).with_suffix('.csv'))
+        if self.filter_file:
+            export_cmd = [
+                self.dm_app, 'traceexport', '-c', '-csv', '-f',
+                self.filter_file, '-b', self.modem_bin, '-o', temp_file, file
+            ]
+        else:
+            export_cmd = [
+                self.dm_app, 'traceexport', '-c', '-csv', '-b', self.modem_bin,
+                '-o', temp_file, file
+            ]
+        logging.debug('Executing: {}'.format(export_cmd))
+        subprocess.call(export_cmd)
+        return temp_file
+
+    def _export_logs(self, log_files):
+        csv_files = []
+        for file in log_files:
+            csv_files.append(self._export_single_log(file))
+        return csv_files
+
+    def _filter_log(self, input_filename, output_filename, write_header):
+        """Export log messages from input file to output file."""
+        log_parser = LogParser()
+        with open(input_filename, 'r') as input_file:
+            with open(output_filename, 'a') as output_file:
+
+                header_line = input_file.readline()
+                log_parser.parse_header(header_line)
+                if log_parser.message_col == -1:
+                    return
+
+                if write_header:
+                    output_file.write(header_line)
+                    # Write next line for time reference.
+                    output_file.write(input_file.readline())
+
+                for line in input_file:
+                    message = log_parser.get_message(line)
+                    if message:
+                        for filter_str in log_parser.PARSER_INFO:
+                            if message.startswith(filter_str):
+                                output_file.write(line)
+                                break
+
+    def _export_filtered_logs(self, csv_files):
+        start_times = []
+        log_parser = LogParser()
+        reordered_csv_files = []
+        for file in csv_files:
+            start_time = log_parser.get_file_start_time(file)
+            if start_time:
+                start_times.append(start_time)
+                reordered_csv_files.append(file)
+        print(reordered_csv_files)
+        print(start_times)
+        file_order = numpy.argsort(start_times)
+        print(file_order)
+        reordered_csv_files = [reordered_csv_files[i] for i in file_order]
+        print(reordered_csv_files)
+        log_directory = Path(reordered_csv_files[0]).parent
+        exported_file = os.path.join(log_directory, 'modem_log.csv')
+        write_header = True
+        for file in reordered_csv_files:
+            self._filter_log(file, exported_file, write_header)
+            write_header = False
+        return exported_file
+
+    def _parse_log(self, log_file, gap_options=0):
+        """Extract required data from the exported CSV file."""
+        log_parser = LogParser()
+        log_data = log_parser.parse_log(log_file, gap_options=0)
+        return log_data
+
+    def process_log(self, log_zip_file):
+        sdm_log_files = self.unzip_modem_logs(log_zip_file)
+        csv_log_files = self._export_logs(sdm_log_files)
+        exported_log = self._export_filtered_logs(csv_log_files)
+        log_data = self._parse_log(exported_log, 0)
+        for file in itertools.chain(sdm_log_files, csv_log_files):
+            os.remove(file)
+        return log_data
diff --git a/acts_tests/acts_contrib/test_utils/fuchsia/bt_test_utils.py b/acts_tests/acts_contrib/test_utils/fuchsia/bt_test_utils.py
index 2c21190..00823b7 100644
--- a/acts_tests/acts_contrib/test_utils/fuchsia/bt_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/fuchsia/bt_test_utils.py
@@ -45,12 +45,12 @@
     """
     if self_manage_scan:
         scan_filter = {"name_substring": search_name}
-        fd.gattc_lib.bleStartBleScan(scan_filter)
+        fd.sl4f.gattc_lib.bleStartBleScan(scan_filter)
     end_time = time.time() + timeout
     found_device = None
     while time.time() < end_time and not found_device:
         time.sleep(1)
-        scan_res = fd.gattc_lib.bleGetDiscoveredDevices()['result']
+        scan_res = fd.sl4f.gattc_lib.bleGetDiscoveredDevices()['result']
         for device in scan_res:
             name, did, connectable = device["name"], device["id"], device[
                 "connectable"]
@@ -59,7 +59,7 @@
                          format(name, did))
                 found_device = device
     if self_manage_scan:
-        fd.gattc_lib.bleStopBleScan()
+        fd.sl4f.gattc_lib.bleStopBleScan()
     if not found_device:
         log.error("Failed to find device with name {}.".format(search_name))
     return found_device
@@ -83,12 +83,12 @@
     Returns:
         The dictionary of device information.
     """
-    fd.bts_lib.requestDiscovery(True)
+    fd.sl4f.bts_lib.requestDiscovery(True)
 
     end_time = time.time() + timeout
     found_device = None
     while time.time() < end_time and not found_device:
-        scan_res = fd.bts_lib.getKnownRemoteDevices()['result']
+        scan_res = fd.sl4f.bts_lib.getKnownRemoteDevices()['result']
         for device in scan_res:
             name, did = scan_res[device]["name"], scan_res[device]["id"]
             if name == search_name or (partial_match and search_name in name):
@@ -96,7 +96,7 @@
                     name, did))
                 found_device = did
         time.sleep(1)
-    fd.bts_lib.requestDiscovery(False)
+    fd.sl4f.bts_lib.requestDiscovery(False)
     if not found_device:
         log.error("Failed to find device with name {}.".format(search_name))
         return found_device
@@ -110,14 +110,14 @@
         fd: The Fuchsia device to unbond devices from.
         log: The log var passed in from the test.
     """
-    fd.bts_lib.requestDiscovery(True)
-    device_list = fd.bts_lib.getKnownRemoteDevices()['result']
-    fd.bts_lib.requestDiscovery(False)
+    fd.sl4f.bts_lib.requestDiscovery(True)
+    device_list = fd.sl4f.bts_lib.getKnownRemoteDevices()['result']
+    fd.sl4f.bts_lib.requestDiscovery(False)
     for device in device_list:
         d = device_list[device]
         if d['bonded'] or d['connected']:
             log.info("Unbonding device: {}".format(d))
-            log.info(fd.bts_lib.forgetDevice(d['id'])['result'])
+            log.info(fd.sl4f.bts_lib.forgetDevice(d['id'])['result'])
 
 
 def verify_device_state_by_name(fd, log, search_name, state, services=None):
@@ -131,13 +131,13 @@
         services: An optional list of services to expect based on the connected
             device.
     """
-    fd.bts_lib.requestDiscovery(True)
+    fd.sl4f.bts_lib.requestDiscovery(True)
 
     seconds_allowed_for_state_change = 10
     end_time = time.time() + seconds_allowed_for_state_change
     found_state = None
     while time.time() < end_time and not found_state:
-        device_list = fd.bts_lib.getKnownRemoteDevices()['result']
+        device_list = fd.sl4f.bts_lib.getKnownRemoteDevices()['result']
         for device in device_list:
             d = device_list[device]
             name = d['name']
@@ -153,7 +153,7 @@
                     break
         time.sleep(1)
     #TODO: Verify services.
-    fd.bts_lib.requestDiscovery(False)
+    fd.sl4f.bts_lib.requestDiscovery(False)
     return found_state
 
 
diff --git a/acts_tests/acts_contrib/test_utils/fuchsia/utils.py b/acts_tests/acts_contrib/test_utils/fuchsia/utils.py
index 8014220..f9837b7 100644
--- a/acts_tests/acts_contrib/test_utils/fuchsia/utils.py
+++ b/acts_tests/acts_contrib/test_utils/fuchsia/utils.py
@@ -15,8 +15,7 @@
 #   limitations under the License.
 
 import os
-
-from acts.libs.proc.job import Error
+from acts.controllers.fuchsia_lib.ssh import FuchsiaSSHError
 
 
 def http_file_download_by_curl(fd,
@@ -48,39 +47,35 @@
     file_path = os.path.join(file_directory, file_name)
     curl_cmd = curl_loc
     if limit_rate:
-        curl_cmd += ' --limit-rate %s' % limit_rate
+        curl_cmd += f' --limit-rate {limit_rate}'
     if retry:
-        curl_cmd += ' --retry %s' % retry
+        curl_cmd += f' --retry {retry}'
     if additional_args:
-        curl_cmd += ' %s' % additional_args
-    curl_cmd += ' --url %s > %s' % (url, file_path)
+        curl_cmd += f' {additional_args}'
+    curl_cmd += f' --url {url} > {file_path}'
+
+    fd.log.info(f'Download {url} to {file_path} by ssh command {curl_cmd}')
     try:
-        fd.log.info(
-            'Download %s to %s by ssh command %s' % (url, file_path, curl_cmd))
+        fd.ssh.run(curl_cmd, timeout_sec=timeout)
+        if _check_file_existence(fd, file_path):
+            fd.log.info(f'{url} is downloaded to {file_path} successfully')
+            return True
 
-        status = fd.send_command_ssh(curl_cmd, timeout=timeout)
-        if isinstance(status, Error):
-            status = status.result
-        if not status.stderr:
-            if int(status.exit_status) != 0:
-                fd.log.warning('Curl command: "%s" failed with error %s' %
-                               (curl_cmd, status.exit_status))
-                return False
-
-            if _check_file_existence(fd, file_path):
-                fd.log.info(
-                    '%s is downloaded to %s successfully' % (url, file_path))
-                return True
-        else:
-            fd.log.warning('Fail to download %s' % url)
-            return False
+        fd.log.warning(f'Fail to download {url}')
+        return False
+    except FuchsiaSSHError as e:
+        fd.log.warning(f'Command "{curl_cmd}" failed with error {e}')
+        return False
     except Exception as e:
-        fd.log.warning('Download %s failed with exception %s' % (url, e))
+        fd.log.error(f'Download {url} failed with unexpected exception {e}')
         return False
     finally:
         if remove_file_after_check:
-            fd.log.info('Remove the downloaded file %s' % file_path)
-            fd.send_command_ssh('rm %s' % file_path)
+            fd.log.info(f'Remove the downloaded file {file_path}')
+            try:
+                fd.ssh.run(f'rm {file_path}')
+            except FuchsiaSSHError:
+                pass
 
 
 def _generate_file_directory_and_file_name(url, out_path):
@@ -113,12 +108,12 @@
         fd: A fuchsia device
         file_path: Where to store the file on the fuchsia device.
     """
-    out = fd.send_command_ssh('ls -al "%s"' % file_path)
-    if isinstance(out, Error):
-        out = out.result
-    if 'No such file or directory' in out.stdout:
-        fd.log.debug('File %s does not exist.' % file_path)
-        return False
-    else:
-        fd.log.debug('File %s exists.' % file_path)
+    try:
+        result = fd.ssh.run(f'ls -al "{file_path}"')
+        fd.log.debug(f'File {file_path} exists.')
         return True
+    except FuchsiaSSHError as e:
+        if 'No such file or directory' in e.result.stderr:
+            fd.log.debug(f'File {file_path} does not exist.')
+            return False
+        raise e
diff --git a/acts_tests/acts_contrib/test_utils/gnss/GnssBlankingBase.py b/acts_tests/acts_contrib/test_utils/gnss/GnssBlankingBase.py
index 9fe9fa4..f6d962f 100644
--- a/acts_tests/acts_contrib/test_utils/gnss/GnssBlankingBase.py
+++ b/acts_tests/acts_contrib/test_utils/gnss/GnssBlankingBase.py
@@ -13,40 +13,39 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
+'''GNSS Base Class for Blanking and Hot Start Sensitivity Search'''
 
 import os
-from glob import glob
+import re
 from time import sleep
 from collections import namedtuple
+from itertools import product
 from numpy import arange
-from pandas import DataFrame
+from pandas import DataFrame, merge
 from acts.signals import TestError
 from acts.signals import TestFailure
 from acts.logger import epoch_to_log_line_timestamp
 from acts.context import get_current_context
 from acts_contrib.test_utils.gnss import LabTtffTestBase as lttb
+from acts_contrib.test_utils.gnss.LabTtffTestBase import glob_re
 from acts_contrib.test_utils.gnss.gnss_test_utils import launch_eecoexer
-from acts_contrib.test_utils.gnss.gnss_test_utils import excute_eecoexer_function
+from acts_contrib.test_utils.gnss.gnss_test_utils import execute_eecoexer_function
 from acts_contrib.test_utils.gnss.gnss_test_utils import start_gnss_by_gtw_gpstool
 from acts_contrib.test_utils.gnss.gnss_test_utils import get_current_epoch_time
 from acts_contrib.test_utils.gnss.gnss_test_utils import check_current_focus_app
 from acts_contrib.test_utils.gnss.gnss_test_utils import process_ttff_by_gtw_gpstool
 from acts_contrib.test_utils.gnss.gnss_test_utils import check_ttff_data
 from acts_contrib.test_utils.gnss.gnss_test_utils import process_gnss_by_gtw_gpstool
-from acts_contrib.test_utils.gnss.gnss_test_utils import start_pixel_logger
-from acts_contrib.test_utils.gnss.gnss_test_utils import stop_pixel_logger
-from acts_contrib.test_utils.gnss.dut_log_test_utils import start_diagmdlog_background
 from acts_contrib.test_utils.gnss.dut_log_test_utils import get_gpstool_logs
-from acts_contrib.test_utils.gnss.dut_log_test_utils import stop_background_diagmdlog
-from acts_contrib.test_utils.gnss.dut_log_test_utils import get_pixellogger_bcm_log
 from acts_contrib.test_utils.gnss.gnss_testlog_utils import parse_gpstool_ttfflog_to_df
 
 
-def range_wi_end(ad, start, stop, step):
+def range_wi_end(dut, start, stop, step):
     """
     Generate a list of data from start to stop with the step. The list includes start and stop value
     and also supports floating point.
     Args:
+        dut: An AndroidDevice object.
         start: start value.
             Type, int or float.
         stop: stop value.
@@ -57,7 +56,7 @@
         range_ls: the list of data.
     """
     if step == 0:
-        ad.log.warn('Step is 0. Return empty list')
+        dut.log.warn('Step is 0. Return empty list')
         range_ls = []
     else:
         if start == stop:
@@ -68,44 +67,50 @@
                 if (step < 0 and range_ls[-1] > stop) or (step > 0 and
                                                           range_ls[-1] < stop):
                     range_ls.append(stop)
+    dut.log.debug(f'The range list is: {range_ls}')
     return range_ls
 
 
-def check_ttff_pe(ad, ttff_data, ttff_mode, pe_criteria):
+def check_ttff_pe(dut, ttff_data, ttff_mode, pe_criteria):
     """Verify all TTFF results from ttff_data.
 
     Args:
-        ad: An AndroidDevice object.
+        dut: An AndroidDevice object.
         ttff_data: TTFF data of secs, position error and signal strength.
         ttff_mode: TTFF Test mode for current test item.
         pe_criteria: Criteria for current test item.
 
     """
     ret = True
-    ad.log.info("%d iterations of TTFF %s tests finished." %
-                (len(ttff_data.keys()), ttff_mode))
-    ad.log.info("%s PASS criteria is %f meters" % (ttff_mode, pe_criteria))
-    ad.log.debug("%s TTFF data: %s" % (ttff_mode, ttff_data))
+    no_iteration = len(ttff_data.keys())
+    dut.log.info(
+        f'{no_iteration} iterations of TTFF {ttff_mode} tests finished.')
+    dut.log.info(f'{ttff_mode} PASS criteria is {pe_criteria} meters')
+    dut.log.debug(f'{ttff_mode} TTFF data: {ttff_data}')
 
     if len(ttff_data.keys()) == 0:
-        ad.log.error("GTW_GPSTool didn't process TTFF properly.")
+        dut.log.error("GTW_GPSTool didn't process TTFF properly.")
         raise TestFailure("GTW_GPSTool didn't process TTFF properly.")
 
     if any(
             float(ttff_data[key].ttff_pe) >= pe_criteria
             for key in ttff_data.keys()):
-        ad.log.error("One or more TTFF %s are over test criteria %f meters" %
-                     (ttff_mode, pe_criteria))
+        dut.log.error(
+            f'One or more TTFF {ttff_mode} are over test criteria {pe_criteria} meters'
+        )
         ret = False
     else:
-        ad.log.info("All TTFF %s are within test criteria %f meters." %
-                    (ttff_mode, pe_criteria))
+        dut.log.info(
+            f'All TTFF {ttff_mode} are within test criteria {pe_criteria} meters.'
+        )
         ret = True
     return ret
 
 
 class GnssBlankingBase(lttb.LabTtffTestBase):
     """ LAB GNSS Cellular Coex Tx Power Sweep TTFF/FFPE Tests"""
+    GNSS_PWR_SWEEP = 'gnss_pwr_sweep'
+    CELL_PWR_SWEEP = 'cell_pwr_sweep'
 
     def __init__(self, controllers):
         """ Initializes class attributes. """
@@ -118,22 +123,28 @@
         self.gsm_sweep_params = None
         self.lte_tdd_pc3_sweep_params = None
         self.lte_tdd_pc2_sweep_params = None
-        self.sa_sensitivity = -150
-        self.gnss_pwr_lvl_offset = -5
-        self.maskfile = None
+        self.coex_stop_cmd = None
+        self.scen_sweep = False
+        self.gnss_pwr_sweep_init_ls = []
+        self.gnss_pwr_sweep_fine_sweep_ls = []
 
     def setup_class(self):
         super().setup_class()
-        req_params = ['sa_sensitivity', 'gnss_pwr_lvl_offset']
+
+        # Required parameters
+        req_params = [self.GNSS_PWR_SWEEP]
         self.unpack_userparams(req_param_names=req_params)
-        cell_sweep_params = self.user_params.get('cell_pwr_sweep', [])
-        self.gsm_sweep_params = cell_sweep_params.get("GSM", [10, 33, 1])
-        self.lte_tdd_pc3_sweep_params = cell_sweep_params.get(
-            "LTE_TDD_PC3", [10, 24, 1])
-        self.lte_tdd_pc2_sweep_params = cell_sweep_params.get(
-            "LTE_TDD_PC2", [10, 26, 1])
-        self.sa_sensitivity = self.user_params.get('sa_sensitivity', -150)
-        self.gnss_pwr_lvl_offset = self.user_params.get('gnss_pwr_lvl_offset', -5)
+        self.unpack_gnss_pwr_sweep()
+
+        # Optional parameters
+        cell_sweep_params = self.user_params.get(self.CELL_PWR_SWEEP, [])
+
+        if cell_sweep_params:
+            self.gsm_sweep_params = cell_sweep_params.get("GSM", [10, 33, 1])
+            self.lte_tdd_pc3_sweep_params = cell_sweep_params.get(
+                "LTE_TDD_PC3", [10, 24, 1])
+            self.lte_tdd_pc2_sweep_params = cell_sweep_params.get(
+                "LTE_TDD_PC2", [10, 26, 1])
 
     def setup_test(self):
         super().setup_test()
@@ -148,11 +159,8 @@
         self.gnss_log_path = os.path.join(self.log_path, cur_test_item_dir)
         os.makedirs(self.gnss_log_path, exist_ok=True)
 
-        # Start GNSS chip log
-        if self.diag_option == "QCOM":
-            start_diagmdlog_background(self.dut, maskfile=self.maskfile)
-        else:
-            start_pixel_logger(self.dut)
+        ## Start GNSS chip log
+        self.start_dut_gnss_log()
 
     def teardown_test(self):
         super().teardown_test()
@@ -161,35 +169,69 @@
                                             self.diag_option)
         os.makedirs(gnss_vendor_log_path, exist_ok=True)
 
-        # Stop GNSS chip log and pull the logs to local file system.
-        if self.diag_option == "QCOM":
-            stop_background_diagmdlog(self.dut,
-                                      gnss_vendor_log_path,
-                                      keep_logs=False)
-        else:
-            stop_pixel_logger(self.dut)
-            self.log.info('Getting Pixel BCM Log!')
-            get_pixellogger_bcm_log(self.dut,
-                                    gnss_vendor_log_path,
-                                    keep_logs=False)
+        # Stop GNSS chip log and pull the logs to local file system
+        self.stop_and_pull_dut_gnss_log(gnss_vendor_log_path)
 
         # Stop cellular Tx and close GPStool and EEcoexer APPs.
-        self.stop_cell_tx()
+        self.stop_coex_tx()
         self.log.debug('Close GPStool APP')
         self.dut.force_stop_apk("com.android.gpstool")
         self.log.debug('Close EEcoexer APP')
         self.dut.force_stop_apk("com.google.eecoexer")
 
-    def stop_cell_tx(self):
+    def derive_sweep_list(self, data):
+        """
+        Derive sweep list from config
+        Args:
+            data: GNSS simulator scenario power setting.
+                type, dictionary.
+        """
+        match_tag = r'(?P<sat>[a-z]+)_(?P<band>[a-z]+\d\S*)'
+        sweep_all_ls = []
+        set_all_ls = []
+        regex_match = re.compile(match_tag)
+        method = data.get('method')
+        for key, value in data.items():
+            result = regex_match.search(key)
+            if result:
+                set_all_ls.append(result.groupdict())
+                sweep_all_ls.append(
+                    range_wi_end(self.dut, value[0], value[1], value[2]))
+        if method == 'product':
+            swp_result_ls = list(product(*sweep_all_ls))
+        else:
+            swp_result_ls = list(zip(*sweep_all_ls))
+
+        self.log.debug(f'set_all_ls: {set_all_ls}')
+        self.log.debug(f'swp_result_ls: {swp_result_ls}')
+        return set_all_ls, swp_result_ls
+
+    def unpack_gnss_pwr_sweep(self):
+        """ Unpack gnss_pwr_sweep and construct sweep parameters
+        """
+
+        for key, value in self.gnss_pwr_sweep.items():
+            if key == 'init':
+                self.gnss_pwr_sweep_init_ls = []
+                self.log.info(f'Sweep: {value}')
+                result = self.derive_sweep_list(value)
+                self.gnss_pwr_sweep_init_ls.append(result)
+            elif key == 'fine_sweep':
+                self.gnss_pwr_sweep_fine_sweep_ls = []
+                self.log.info(f'Sweep: {value}')
+                result = self.derive_sweep_list(value)
+                self.gnss_pwr_sweep_fine_sweep_ls.append(result)
+            else:
+                self.log.error(f'{key} is a unsupported key in gnss_pwr_sweep.')
+
+    def stop_coex_tx(self):
         """
         Stop EEcoexer Tx power.
         """
-        # EEcoexer cellular stop Tx command.
-        stop_cell_tx_cmd = 'CELLR,19'
-
         # Stop cellular Tx by EEcoexer.
-        self.log.info('Stop EEcoexer Test Command: {}'.format(stop_cell_tx_cmd))
-        excute_eecoexer_function(self.dut, stop_cell_tx_cmd)
+        if self.coex_stop_cmd:
+            self.log.info(f'Stop EEcoexer Test Command: {self.coex_stop_cmd}')
+            execute_eecoexer_function(self.dut, self.coex_stop_cmd)
 
     def analysis_ttff_ffpe(self, ttff_data, json_tag=''):
         """
@@ -208,45 +250,81 @@
         get_gpstool_logs(self.dut, gps_log_path, False)
 
         # Parsing the log of GTW GPStool into pandas dataframe.
-        target_log_name_regx = os.path.join(gps_log_path, 'GPSLogs', 'files',
-                                            'GNSS_*')
-        self.log.info('Get GPStool logs from: {}'.format(target_log_name_regx))
-        gps_api_log_ls = glob(target_log_name_regx)
+        target_dir = os.path.join(gps_log_path, 'GPSLogs', 'files')
+        gps_api_log_ls = glob_re(self.dut, target_dir, r'GNSS_\d+')
         latest_gps_api_log = max(gps_api_log_ls, key=os.path.getctime)
-        self.log.info(
-            'Get latest GPStool log is: {}'.format(latest_gps_api_log))
+        self.log.info(f'Get latest GPStool log is: {latest_gps_api_log}')
+        df_ttff_ffpe = DataFrame(
+            parse_gpstool_ttfflog_to_df(latest_gps_api_log))
+        # Add test case, TTFF and FFPE data into the dataframe.
+        ttff_dict = {}
+        for i in ttff_data:
+            data = ttff_data[i]._asdict()
+            ttff_dict[i] = dict(data)
+
+        ttff_data_df = DataFrame(ttff_dict).transpose()
+        ttff_data_df = ttff_data_df[[
+            'ttff_loop', 'ttff_sec', 'ttff_pe', 'ttff_haccu'
+        ]]
         try:
-            df_ttff_ffpe = DataFrame(
-                parse_gpstool_ttfflog_to_df(latest_gps_api_log))
+            df_ttff_ffpe = merge(df_ttff_ffpe,
+                                 ttff_data_df,
+                                 left_on='loop',
+                                 right_on='ttff_loop')
+        except:  # pylint: disable=bare-except
+            self.log.warning("Can't merge ttff_data and df.")
+        df_ttff_ffpe['test_case'] = json_tag
 
-            # Add test case, TTFF and FFPE data into the dataframe.
-            ttff_dict = {}
-            for i in ttff_data:
-                data = ttff_data[i]._asdict()
-                ttff_dict[i] = dict(data)
-            ttff_time = []
-            ttff_pe = []
-            test_case = []
-            for value in ttff_dict.values():
-                ttff_time.append(value['ttff_sec'])
-                ttff_pe.append(value['ttff_pe'])
-                test_case.append(json_tag)
-            self.log.info('test_case length {}'.format(str(len(test_case))))
+        json_file = f'gps_log_{json_tag}.json'
+        ttff_data_json_file = f'gps_log_{json_tag}_ttff_data.json'
+        json_path = os.path.join(gps_log_path, json_file)
+        ttff_data_json_path = os.path.join(gps_log_path, ttff_data_json_file)
+        # Save dataframe into json file.
+        df_ttff_ffpe.to_json(json_path, orient='table', index=False)
+        ttff_data_df.to_json(ttff_data_json_path, orient='table', index=False)
 
-            df_ttff_ffpe['test_case'] = test_case
-            df_ttff_ffpe['ttff_sec'] = ttff_time
-            df_ttff_ffpe['ttff_pe'] = ttff_pe
-            json_file = 'gps_log_{}.json'.format(json_tag)
-            json_path = os.path.join(gps_log_path, json_file)
-            # Save dataframe into json file.
-            df_ttff_ffpe.to_json(json_path, orient='table', index=False)
-        except ValueError:
-            self.log.warning('Can\'t create the parsed the log data in file.')
+    def hot_start_ttff_ffpe_process(self, iteration, wait):
+        """
+        Function to run hot start TTFF/FFPE by GTW GPSTool
+        Args:
+            iteration: TTFF/FFPE test iteration.
+                type, integer.
+            wait: wait time before the hot start TTFF/FFPE test.
+                type, integer.
+        """
+        # Start GTW GPStool.
+        self.dut.log.info("Restart GTW GPSTool")
+        start_gnss_by_gtw_gpstool(self.dut, state=True)
+        if wait > 0:
+            self.log.info(
+                f'Wait for {wait} seconds before TTFF to acquire data.')
+            sleep(wait)
+        # Get current time and convert to human readable format
+        begin_time = get_current_epoch_time()
+        log_begin_time = epoch_to_log_line_timestamp(begin_time)
+        self.dut.log.debug(f'Start time is {log_begin_time}')
+
+        # Run hot start TTFF
+        for i in range(3):
+            self.log.info(f'Start hot start attempt {i + 1}')
+            self.dut.adb.shell(
+                f'am broadcast -a com.android.gpstool.ttff_action '
+                f'--es ttff hs --es cycle {iteration} --ez raninterval False')
+            sleep(1)
+            if self.dut.search_logcat(
+                    "act=com.android.gpstool.start_test_action", begin_time):
+                self.dut.log.info("Send TTFF start_test_action successfully.")
+                break
+        else:
+            check_current_focus_app(self.dut)
+            raise TestError("Fail to send TTFF start_test_action.")
+        return begin_time
 
     def gnss_hot_start_ttff_ffpe_test(self,
                                       iteration,
                                       sweep_enable=False,
-                                      json_tag=''):
+                                      json_tag='',
+                                      wait=0):
         """
         GNSS hot start ttff ffpe tset
 
@@ -261,40 +339,26 @@
                     this as a part of file name to save TTFF and FFPE results into json file.
                     Type, str.
                     Default, ''.
+            wait: wait time before ttff test.
+                    Type, int.
+                    Default, 0.
         Raise:
             TestError: fail to send TTFF start_test_action.
         """
-        # Start GTW GPStool.
         test_type = namedtuple('Type', ['command', 'criteria'])
-        test_type_ttff = test_type('Hot Start', self.hs_ttff_criteria)
+        test_type_ttff = test_type('Hot Start', self.hs_criteria)
         test_type_pe = test_type('Hot Start', self.hs_ttff_pecriteria)
-        self.dut.log.info("Restart GTW GPSTool")
-        start_gnss_by_gtw_gpstool(self.dut, state=True)
-
-        # Get current time and convert to human readable format
-        begin_time = get_current_epoch_time()
-        log_begin_time = epoch_to_log_line_timestamp(begin_time)
-        self.dut.log.debug('Start time is {}'.format(log_begin_time))
-
-        # Run hot start TTFF
-        for i in range(3):
-            self.log.info('Start hot start attempt %d' % (i + 1))
-            self.dut.adb.shell(
-                "am broadcast -a com.android.gpstool.ttff_action "
-                "--es ttff hs --es cycle {} --ez raninterval False".format(
-                    iteration))
-            sleep(1)
-            if self.dut.search_logcat(
-                    "act=com.android.gpstool.start_test_action", begin_time):
-                self.dut.log.info("Send TTFF start_test_action successfully.")
-                break
-        else:
-            check_current_focus_app(self.dut)
-            raise TestError("Fail to send TTFF start_test_action.")
 
         # Verify hot start TTFF results
-        ttff_data = process_ttff_by_gtw_gpstool(self.dut, begin_time,
-                                                self.simulator_location)
+        begin_time = self.hot_start_ttff_ffpe_process(iteration, wait)
+        try:
+            ttff_data = process_ttff_by_gtw_gpstool(self.dut, begin_time,
+                                                    self.simulator_location)
+        except:  # pylint: disable=bare-except
+            self.log.warning('Fail to acquire TTFF data. Retry again.')
+            begin_time = self.hot_start_ttff_ffpe_process(iteration, wait)
+            ttff_data = process_ttff_by_gtw_gpstool(self.dut, begin_time,
+                                                    self.simulator_location)
 
         # Stop GTW GPSTool
         self.dut.log.info("Stop GTW GPSTool")
@@ -320,10 +384,8 @@
         return True
 
     def hot_start_gnss_power_sweep(self,
-                                   start_pwr,
-                                   stop_pwr,
-                                   offset,
-                                   wait,
+                                   sweep_ls,
+                                   wait=0,
                                    iteration=1,
                                    sweep_enable=False,
                                    title=''):
@@ -331,14 +393,11 @@
         GNSS simulator power sweep of hot start test.
 
         Args:
-            start_pwr: GNSS simulator power sweep start power level.
-                    Type, int.
-            stop_pwr: GNSS simulator power sweep stop power level.
-                    Type, int.
-            offset: GNSS simulator power sweep offset
-                    Type, int.
+            sweep_ls: list of sweep parameters.
+                    Type, tuple.
             wait: Wait time before the power sweep.
                     Type, int.
+                    Default, 0.
             iteration: The iteration times of hot start test.
                     Type, int.
                     Default, 1.
@@ -349,15 +408,15 @@
             title: the target log folder title for GNSS sensitivity search test items.
                     Type, str.
                     Default, ''.
+        Return:
+            Bool, gnss_pwr_params.
         """
 
         # Calculate loop range list from gnss_simulator_power_level and sa_sensitivity
-        range_ls = range_wi_end(self.dut, start_pwr, stop_pwr, offset)
-        sweep_range = ','.join([str(x) for x in range_ls])
 
         self.log.debug(
-            'Start the GNSS simulator power sweep. The sweep range is [{}]'.
-            format(sweep_range))
+            f'Start the GNSS simulator power sweep. The sweep tuple is [{sweep_ls}]'
+        )
 
         if sweep_enable:
             self.start_gnss_and_wait(wait)
@@ -368,21 +427,41 @@
         # Sweep GNSS simulator power level in range_ls.
         # Do hot start for every power level.
         # Check the TTFF result if it can pass the criteria.
-        gnss_pwr_lvl = -130
-        for gnss_pwr_lvl in range_ls:
-
-            # Set GNSS Simulator power level
-            self.log.info('Set GNSS simulator power level to %.1f' %
-                          gnss_pwr_lvl)
-            self.gnss_simulator.set_power(gnss_pwr_lvl)
-            json_tag = title + '_gnss_pwr_' + str(gnss_pwr_lvl)
-
+        gnss_pwr_params = None
+        previous_pwr_lvl = None
+        current_pwr_lvl = None
+        return_pwr_lvl = {}
+        for j, gnss_pwr_params in enumerate(sweep_ls[1]):
+            json_tag = f'{title}_'
+            current_pwr_lvl = gnss_pwr_params
+            if j == 0:
+                previous_pwr_lvl = current_pwr_lvl
+            for i, pwr in enumerate(gnss_pwr_params):
+                sat_sys = sweep_ls[0][i].get('sat').upper()
+                band = sweep_ls[0][i].get('band').upper()
+                # Set GNSS Simulator power level
+                self.gnss_simulator.ping_inst()
+                self.gnss_simulator.set_scenario_power(power_level=pwr,
+                                                       sat_system=sat_sys,
+                                                       freq_band=band)
+                self.log.info(f'Set {sat_sys} {band} with power {pwr}')
+                json_tag = json_tag + f'{sat_sys}_{band}_{pwr}'
+            # Wait 30 seconds if major power sweep level is changed.
+            wait = 0
+            if j > 0:
+                if current_pwr_lvl[0] != previous_pwr_lvl[0]:
+                    wait = 30
             # GNSS hot start test
             if not self.gnss_hot_start_ttff_ffpe_test(iteration, sweep_enable,
-                                                      json_tag):
-                sensitivity = gnss_pwr_lvl - offset
-                return False, sensitivity
-        return True, gnss_pwr_lvl
+                                                      json_tag, wait):
+                result = False
+                break
+            previous_pwr_lvl = current_pwr_lvl
+        result = True
+        for i, pwr in enumerate(previous_pwr_lvl):
+            key = f'{sweep_ls[0][i].get("sat").upper()}_{sweep_ls[0][i].get("band").upper()}'
+            return_pwr_lvl.setdefault(key, pwr)
+        return result, return_pwr_lvl
 
     def gnss_init_power_setting(self, first_wait=180):
         """
@@ -398,39 +477,22 @@
         """
 
         # Start and set GNSS simulator
-        self.start_and_set_gnss_simulator_power()
+        self.start_set_gnss_power()
 
         # Start 1st time cold start to obtain ephemeris
         process_gnss_by_gtw_gpstool(self.dut, self.test_types['cs'].criteria)
 
-        self.hot_start_gnss_power_sweep(self.gnss_simulator_power_level,
-                                        self.sa_sensitivity,
-                                        self.gnss_pwr_lvl_offset, first_wait)
+        # Read initial power sweep settings
+        if self.gnss_pwr_sweep_init_ls:
+            for sweep_ls in self.gnss_pwr_sweep_init_ls:
+                ret, gnss_pwr_lvl = self.hot_start_gnss_power_sweep(
+                    sweep_ls, first_wait)
+        else:
+            self.log.warning('Skip initial power sweep.')
+            ret = False
+            gnss_pwr_lvl = None
 
-        return True
-
-    def start_gnss_and_wait(self, wait=60):
-        """
-        The process of enable gnss and spend the wait time for GNSS to
-        gather enoung information that make sure the stability of testing.
-
-        Args:
-            wait: wait time between power sweep.
-                Type, int.
-                Default, 60.
-        """
-        # Create log path for waiting section logs of GPStool.
-        gnss_wait_log_dir = os.path.join(self.gnss_log_path, 'GNSS_wait')
-
-        # Enable GNSS to receive satellites' signals for "wait_between_pwr" seconds.
-        self.log.info('Enable GNSS for searching satellites')
-        start_gnss_by_gtw_gpstool(self.dut, state=True)
-        self.log.info('Wait for {} seconds'.format(str(wait)))
-        sleep(wait)
-
-        # Stop GNSS and pull the logs.
-        start_gnss_by_gtw_gpstool(self.dut, state=False)
-        get_gpstool_logs(self.dut, gnss_wait_log_dir, False)
+        return ret, gnss_pwr_lvl
 
     def cell_power_sweep(self):
         """
@@ -449,9 +511,6 @@
         power_search_ls = range_wi_end(self.dut, self.start_pwr, self.stop_pwr,
                                        self.offset)
 
-        # Set GNSS simulator power level.
-        self.gnss_simulator.set_power(self.sa_sensitivity)
-
         # Create gnss log folders for init and cellular sweep
         gnss_init_log_dir = os.path.join(self.gnss_log_path, 'GNSS_init')
 
@@ -461,8 +520,8 @@
         if power_search_ls:
             # Run the cellular and GNSS coexistence test item.
             for i, pwr_lvl in enumerate(power_search_ls):
-                self.log.info('Cellular power sweep loop: {}'.format(int(i)))
-                self.log.info('Cellular target power: {}'.format(int(pwr_lvl)))
+                self.log.info(f'Cellular power sweep loop: {i}')
+                self.log.info(f'Cellular target power: {pwr_lvl}')
 
                 # Enable GNSS to receive satellites' signals for "wait_between_pwr" seconds.
                 # Wait more time before 1st power level
@@ -473,9 +532,9 @@
                 self.start_gnss_and_wait(wait)
 
                 # Set cellular Tx power level.
-                eecoex_cmd = self.eecoex_func.format(str(pwr_lvl))
+                eecoex_cmd = self.eecoex_func.format(pwr_lvl)
                 eecoex_cmd_file_str = eecoex_cmd.replace(',', '_')
-                excute_eecoexer_function(self.dut, eecoex_cmd)
+                execute_eecoexer_function(self.dut, eecoex_cmd)
 
                 # Get the last power level that can pass hots start ttff/ffpe spec.
                 if self.gnss_hot_start_ttff_ffpe_test(ttft_iteration, True,
@@ -489,7 +548,7 @@
                         power_th = power_search_ls[i - 1]
 
                 # Stop cellular Tx after a test cycle.
-                self.stop_cell_tx()
+                self.stop_coex_tx()
 
         else:
             # Run the stand alone test item.
@@ -499,7 +558,6 @@
             self.gnss_hot_start_ttff_ffpe_test(ttft_iteration, True,
                                                eecoex_cmd_file_str)
 
-        self.log.info('The GNSS WWAN coex celluar Tx power is {}'.format(
-            str(power_th)))
+        self.log.info(f'The GNSS WWAN coex celluar Tx power is {power_th}')
 
         return power_th
diff --git a/acts_tests/acts_contrib/test_utils/gnss/LabTtffTestBase.py b/acts_tests/acts_contrib/test_utils/gnss/LabTtffTestBase.py
index 6a6bd5d..77ebcf6 100644
--- a/acts_tests/acts_contrib/test_utils/gnss/LabTtffTestBase.py
+++ b/acts_tests/acts_contrib/test_utils/gnss/LabTtffTestBase.py
@@ -13,35 +13,51 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
+'''GNSS Base Class for Lab TTFF/FFPE'''
 
 import os
 import time
-import glob
 import errno
+import re
 from collections import namedtuple
-from pandas import DataFrame
-from acts import utils
-from acts import signals
-from acts.base_test import BaseTestClass
-from acts.controllers.gnss_lib import GnssSimulator
-from acts.context import get_current_context
-from acts_contrib.test_utils.gnss import dut_log_test_utils as diaglog
-from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
-from acts_contrib.test_utils.gnss import gnss_testlog_utils as glogutils
+from pandas import DataFrame, merge
 from acts_contrib.test_utils.gnss.gnss_defines import DEVICE_GPSLOG_FOLDER
 from acts_contrib.test_utils.gnss.gnss_defines import GPS_PKG_NAME
 from acts_contrib.test_utils.gnss.gnss_defines import BCM_GPS_XML_PATH
+from acts_contrib.test_utils.gnss import dut_log_test_utils as diaglog
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
+from acts_contrib.test_utils.gnss import gnss_testlog_utils as glogutils
+from acts import utils
+from acts import signals
+from acts.controllers.gnss_lib import GnssSimulator
+from acts.context import get_current_context
+from acts.base_test import BaseTestClass
+
+
+def glob_re(dut, directory, regex_tag):
+    """glob with regular expression method.
+    Args:
+        dut: An AndroidDevice object.
+        directory: Target directory path.
+           Type, str
+        regex_tag: regular expression format string.
+           Type, str
+    Return:
+        result_ls: list of glob result
+    """
+    all_files_in_dir = os.listdir(directory)
+    dut.log.debug(f'glob_re dir: {all_files_in_dir}')
+    target_log_name_regx = re.compile(regex_tag)
+    tmp_ls = list(filter(target_log_name_regx.match, all_files_in_dir))
+    result_ls = [os.path.join(directory, file) for file in tmp_ls]
+    dut.log.debug(f'glob_re list: {result_ls}')
+    return result_ls
 
 
 class LabTtffTestBase(BaseTestClass):
     """ LAB TTFF Tests Base Class"""
     GTW_GPSTOOL_APP = 'gtw_gpstool_apk'
-    GNSS_SIMULATOR_KEY = 'gnss_simulator'
-    GNSS_SIMULATOR_IP_KEY = 'gnss_simulator_ip'
-    GNSS_SIMULATOR_PORT_KEY = 'gnss_simulator_port'
-    GNSS_SIMULATOR_PORT_CTRL_KEY = 'gnss_simulator_port_ctrl'
-    GNSS_SIMULATOR_SCENARIO_KEY = 'gnss_simulator_scenario'
-    GNSS_SIMULATOR_POWER_LEVEL_KEY = 'gnss_simulator_power_level'
+    GNSS_SIMULATOR_KEY = 'gnss_sim_params'
     CUSTOM_FILES_KEY = 'custom_files'
     CSTTFF_CRITERIA = 'cs_criteria'
     HSTTFF_CRITERIA = 'hs_criteria'
@@ -52,76 +68,106 @@
     TTFF_ITERATION = 'ttff_iteration'
     SIMULATOR_LOCATION = 'simulator_location'
     DIAG_OPTION = 'diag_option'
+    SCENARIO_POWER = 'scenario_power'
+    MDSAPP = 'mdsapp'
+    MASKFILE = 'maskfile'
+    MODEMPARFILE = 'modemparfile'
+    NV_DICT = 'nv_dict'
+    TTFF_TIMEOUT = 'ttff_timeout'
 
     def __init__(self, controllers):
         """ Initializes class attributes. """
 
         super().__init__(controllers)
-
         self.dut = None
         self.gnss_simulator = None
         self.rockbottom_script = None
         self.gnss_log_path = self.log_path
         self.gps_xml_bk_path = BCM_GPS_XML_PATH + '.bk'
+        self.gpstool_ver = ''
+        self.test_params = None
+        self.custom_files = None
+        self.maskfile = None
+        self.mdsapp = None
+        self.modemparfile = None
+        self.nv_dict = None
+        self.scenario_power = None
+        self.ttff_timeout = None
+        self.test_types = None
+        self.simulator_location = None
+        self.gnss_simulator_scenario = None
+        self.gnss_simulator_power_level = None
 
     def setup_class(self):
         super().setup_class()
 
-        req_params = [
-            self.GNSS_SIMULATOR_KEY, self.GNSS_SIMULATOR_IP_KEY,
-            self.GNSS_SIMULATOR_PORT_KEY, self.GNSS_SIMULATOR_SCENARIO_KEY,
-            self.GNSS_SIMULATOR_POWER_LEVEL_KEY, self.CSTTFF_CRITERIA,
-            self.HSTTFF_CRITERIA, self.WSTTFF_CRITERIA, self.TTFF_ITERATION,
-            self.SIMULATOR_LOCATION, self.DIAG_OPTION
-        ]
+        # Update parameters by test case configurations.
+        test_param = self.TAG + '_params'
+        self.test_params = self.user_params.get(test_param, {})
+        if not self.test_params:
+            self.log.warning(test_param + ' was not found in the user '
+                             'parameters defined in the config file.')
 
+        # Override user_param values with test parameters
+        self.user_params.update(self.test_params)
+
+        # Unpack user_params with default values. All the usages of user_params
+        # as self attributes need to be included either as a required parameter
+        # or as a parameter with a default value.
+
+        # Required parameters
+        req_params = [
+            self.CSTTFF_PECRITERIA, self.WSTTFF_PECRITERIA, self.HSTTFF_PECRITERIA,
+            self.CSTTFF_CRITERIA, self.HSTTFF_CRITERIA, self.WSTTFF_CRITERIA,
+            self.TTFF_ITERATION, self.GNSS_SIMULATOR_KEY, self.DIAG_OPTION,
+            self.GTW_GPSTOOL_APP
+        ]
         self.unpack_userparams(req_param_names=req_params)
+
+        # Optional parameters
+        self.custom_files = self.user_params.get(self.CUSTOM_FILES_KEY,[])
+        self.maskfile = self.user_params.get(self.MASKFILE,'')
+        self.mdsapp = self.user_params.get(self.MDSAPP,'')
+        self.modemparfile = self.user_params.get(self.MODEMPARFILE,'')
+        self.nv_dict = self.user_params.get(self.NV_DICT,{})
+        self.scenario_power = self.user_params.get(self.SCENARIO_POWER, [])
+        self.ttff_timeout = self.user_params.get(self.TTFF_TIMEOUT, 60)
+
+        # Set TTFF Spec.
+        test_type = namedtuple('Type', ['command', 'criteria'])
+        self.test_types = {
+            'cs': test_type('Cold Start', self.cs_criteria),
+            'ws': test_type('Warm Start', self.ws_criteria),
+            'hs': test_type('Hot Start', self.hs_criteria)
+        }
+
         self.dut = self.android_devices[0]
-        self.gnss_simulator_scenario = self.user_params[
-            self.GNSS_SIMULATOR_SCENARIO_KEY]
-        self.gnss_simulator_power_level = self.user_params[
-            self.GNSS_SIMULATOR_POWER_LEVEL_KEY]
-        self.gtw_gpstool_app = self.user_params[self.GTW_GPSTOOL_APP]
-        custom_files = self.user_params.get(self.CUSTOM_FILES_KEY, [])
-        self.cs_ttff_criteria = self.user_params.get(self.CSTTFF_CRITERIA, [])
-        self.hs_ttff_criteria = self.user_params.get(self.HSTTFF_CRITERIA, [])
-        self.ws_ttff_criteria = self.user_params.get(self.WSTTFF_CRITERIA, [])
-        self.cs_ttff_pecriteria = self.user_params.get(self.CSTTFF_PECRITERIA,
-                                                       [])
-        self.hs_ttff_pecriteria = self.user_params.get(self.HSTTFF_PECRITERIA,
-                                                       [])
-        self.ws_ttff_pecriteria = self.user_params.get(self.WSTTFF_PECRITERIA,
-                                                       [])
-        self.ttff_iteration = self.user_params.get(self.TTFF_ITERATION, [])
-        self.simulator_location = self.user_params.get(self.SIMULATOR_LOCATION,
-                                                       [])
-        self.diag_option = self.user_params.get(self.DIAG_OPTION, [])
+
+        # GNSS Simulator Setup
+        self.simulator_location = self.gnss_sim_params.get(
+            self.SIMULATOR_LOCATION, [])
+        self.gnss_simulator_scenario = self.gnss_sim_params.get('scenario')
+        self.gnss_simulator_power_level = self.gnss_sim_params.get(
+            'power_level')
 
         # Create gnss_simulator instance
-        gnss_simulator_key = self.user_params[self.GNSS_SIMULATOR_KEY]
-        gnss_simulator_ip = self.user_params[self.GNSS_SIMULATOR_IP_KEY]
-        gnss_simulator_port = self.user_params[self.GNSS_SIMULATOR_PORT_KEY]
+        gnss_simulator_key = self.gnss_sim_params.get('type')
+        gnss_simulator_ip = self.gnss_sim_params.get('ip')
+        gnss_simulator_port = self.gnss_sim_params.get('port')
         if gnss_simulator_key == 'gss7000':
-            gnss_simulator_port_ctrl = self.user_params[
-                self.GNSS_SIMULATOR_PORT_CTRL_KEY]
+            gnss_simulator_port_ctrl = self.gnss_sim_params.get('port_ctrl')
         else:
             gnss_simulator_port_ctrl = None
         self.gnss_simulator = GnssSimulator.AbstractGnssSimulator(
             gnss_simulator_key, gnss_simulator_ip, gnss_simulator_port,
             gnss_simulator_port_ctrl)
 
-        test_type = namedtuple('Type', ['command', 'criteria'])
-        self.test_types = {
-            'cs': test_type('Cold Start', self.cs_ttff_criteria),
-            'ws': test_type('Warm Start', self.ws_ttff_criteria),
-            'hs': test_type('Hot Start', self.hs_ttff_criteria)
-        }
-
         # Unpack the rockbottom script file if its available.
-        for file in custom_files:
-            if 'rockbottom_' + self.dut.model in file:
-                self.rockbottom_script = file
-                break
+        if self.custom_files:
+            for file in self.custom_files:
+                if 'rockbottom_' + self.dut.model in file:
+                    self.rockbottom_script = file
+                    break
 
     def setup_test(self):
 
@@ -129,25 +175,43 @@
         self.gnss_simulator.stop_scenario()
         self.gnss_simulator.close()
         if self.rockbottom_script:
-            self.log.info('Running rockbottom script for this device ' +
-                          self.dut.model)
+            self.log.info(
+                f'Running rockbottom script for this device {self.dut.model}')
             self.dut_rockbottom()
         else:
-            self.log.info('Not running rockbottom for this device ' +
-                          self.dut.model)
+            self.log.info(
+                f'Not running rockbottom for this device {self.dut.model}')
 
         utils.set_location_service(self.dut, True)
         gutils.reinstall_package_apk(self.dut, GPS_PKG_NAME,
-                                     self.gtw_gpstool_app)
+                                     self.gtw_gpstool_apk)
+        gpstool_ver_cmd = f'dumpsys package {GPS_PKG_NAME} | grep versionName'
+        self.gpstool_ver = self.dut.adb.shell(gpstool_ver_cmd).split('=')[1]
+        self.log.info(f'GTW GPSTool version: {self.gpstool_ver}')
 
         # For BCM DUTs, delete gldata.sto and set IgnoreRomAlm="true" based on b/196936791#comment20
         if self.diag_option == "BCM":
             gutils.remount_device(self.dut)
             # Backup gps.xml
-            copy_cmd = "cp {} {}".format(BCM_GPS_XML_PATH, self.gps_xml_bk_path)
+            if self.dut.file_exists(BCM_GPS_XML_PATH):
+                copy_cmd = f'cp {BCM_GPS_XML_PATH} {self.gps_xml_bk_path}'
+            elif self.dut.file_exists(self.gps_xml_bk_path):
+                self.log.debug(f'{BCM_GPS_XML_PATH} is missing')
+                self.log.debug(
+                    f'Copy {self.gps_xml_bk_path} and rename to {BCM_GPS_XML_PATH}'
+                )
+                copy_cmd = f'cp {self.gps_xml_bk_path} {BCM_GPS_XML_PATH}'
+            else:
+                self.log.error(
+                    f'Missing both {BCM_GPS_XML_PATH} and {self.gps_xml_bk_path} in DUT'
+                )
+                raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT),
+                                        self.gps_xml_bk_path)
             self.dut.adb.shell(copy_cmd)
             gutils.delete_bcm_nvmem_sto_file(self.dut)
             gutils.bcm_gps_ignore_rom_alm(self.dut)
+            if self.current_test_name == "test_tracking_power_sweep":
+                gutils.bcm_gps_ignore_warmstandby(self.dut)
             # Reboot DUT to apply the setting
             gutils.reboot(self.dut)
         self.gnss_simulator.connect()
@@ -160,9 +224,9 @@
         # The rockbottom script might include a device reboot, so it is
         # necessary to stop SL4A during its execution.
         self.dut.stop_services()
-        self.log.info('Executing rockbottom script for ' + self.dut.model)
+        self.log.info(f'Executing rockbottom script for {self.dut.model}')
         os.chmod(self.rockbottom_script, 0o777)
-        os.system('{} {}'.format(self.rockbottom_script, self.dut.serial))
+        os.system(f'{self.rockbottom_script} {self.dut.serial}')
         # Make sure the DUT is in root mode after coming back
         self.dut.root_adb()
         # Restart SL4A
@@ -174,9 +238,9 @@
         # Restore the gps.xml everytime after the test.
         if self.diag_option == "BCM":
             # Restore gps.xml
-            rm_cmd = "rm -rf {}".format(BCM_GPS_XML_PATH)
-            restore_cmd = "mv {} {}".format(self.gps_xml_bk_path,
-                                            BCM_GPS_XML_PATH)
+            gutils.remount_device(self.dut)
+            rm_cmd = f'rm -rf {BCM_GPS_XML_PATH}'
+            restore_cmd = f'cp {self.gps_xml_bk_path} {BCM_GPS_XML_PATH}'
             self.dut.adb.shell(rm_cmd)
             self.dut.adb.shell(restore_cmd)
 
@@ -187,7 +251,7 @@
             self.gnss_simulator.stop_scenario()
             self.gnss_simulator.close()
 
-    def start_and_set_gnss_simulator_power(self):
+    def start_set_gnss_power(self):
         """
         Start GNSS simulator secnario and set power level.
 
@@ -195,7 +259,24 @@
 
         self.gnss_simulator.start_scenario(self.gnss_simulator_scenario)
         time.sleep(25)
-        self.gnss_simulator.set_power(self.gnss_simulator_power_level)
+        if self.scenario_power:
+            self.log.info(
+                'Set GNSS simulator power with power_level by scenario_power')
+            for setting in self.scenario_power:
+                power_level = setting.get('power_level', -130)
+                sat_system = setting.get('sat_system', '')
+                freq_band = setting.get('freq_band', 'ALL')
+                sat_id = setting.get('sat_id', '')
+                self.log.debug(f'sat: {sat_system}; freq_band: {freq_band}, '
+                               f'power_level: {power_level}, sat_id: {sat_id}')
+                self.gnss_simulator.set_scenario_power(power_level,
+                                                       sat_id,
+                                                       sat_system,
+                                                       freq_band)
+        else:
+            self.log.debug('Set GNSS simulator power '
+                           f'with power_level: {self.gnss_simulator_power_level}')
+            self.gnss_simulator.set_power(self.gnss_simulator_power_level)
 
     def get_and_verify_ttff(self, mode):
         """Retrieve ttff with designate mode.
@@ -204,14 +285,9 @@
                 mode: A string for identify gnss test mode.
         """
         if mode not in self.test_types:
-            raise signals.TestError('Unrecognized mode %s' % mode)
+            raise signals.TestError(f'Unrecognized mode {mode}')
         test_type = self.test_types.get(mode)
 
-        if mode != 'cs':
-            wait_time = 900
-        else:
-            wait_time = 300
-
         gutils.process_gnss_by_gtw_gpstool(self.dut,
                                            self.test_types['cs'].criteria)
         begin_time = gutils.get_current_epoch_time()
@@ -219,12 +295,12 @@
                                          ttff_mode=mode,
                                          iteration=self.ttff_iteration,
                                          raninterval=True,
-                                         hot_warm_sleep=wait_time)
+                                         hot_warm_sleep=3,
+                                         timeout=self.ttff_timeout)
         # Since Wear takes little longer to update the TTFF info.
         # Workround to solve the wearable timing issue
         if gutils.is_device_wearable(self.dut):
             time.sleep(20)
-
         ttff_data = gutils.process_ttff_by_gtw_gpstool(self.dut, begin_time,
                                                        self.simulator_location)
 
@@ -232,41 +308,43 @@
         gps_log_path = os.path.join(self.gnss_log_path, 'GPSLogs')
         os.makedirs(gps_log_path, exist_ok=True)
 
-        self.dut.adb.pull("{} {}".format(DEVICE_GPSLOG_FOLDER, gps_log_path))
-
-        gps_api_log = glob.glob(gps_log_path + '/*/GNSS_*.txt')
-        ttff_loop_log = glob.glob(gps_log_path +
-                                  '/*/GPS_{}_*.txt'.format(mode.upper()))
+        self.dut.adb.pull(f'{DEVICE_GPSLOG_FOLDER} {gps_log_path}')
+        local_log_dir = os.path.join(gps_log_path, 'files')
+        gps_api_log = glob_re(self.dut, local_log_dir, r'GNSS_\d+')
+        ttff_loop_log = glob_re(self.dut, local_log_dir,
+                                fr'\w+_{mode.upper()}_\d+')
 
         if not gps_api_log and ttff_loop_log:
             raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT),
                                     gps_log_path)
 
-        df = DataFrame(glogutils.parse_gpstool_ttfflog_to_df(gps_api_log[0]))
+        df_ttff_ffpe = DataFrame(glogutils.parse_gpstool_ttfflog_to_df(gps_api_log[0]))
 
         ttff_dict = {}
         for i in ttff_data:
-            d = ttff_data[i]._asdict()
-            ttff_dict[i] = dict(d)
+            data = ttff_data[i]._asdict()
+            ttff_dict[i] = dict(data)
 
-        ttff_time = []
-        ttff_pe = []
-        ttff_haccu = []
-        for i in ttff_dict.keys():
-            ttff_time.append(ttff_dict[i]['ttff_sec'])
-            ttff_pe.append(ttff_dict[i]['ttff_pe'])
-            ttff_haccu.append(ttff_dict[i]['ttff_haccu'])
-        df['ttff_sec'] = ttff_time
-        df['ttff_pe'] = ttff_pe
-        df['ttff_haccu'] = ttff_haccu
-        df.to_json(gps_log_path + '/gps_log.json', orient='table')
+        ttff_data_df = DataFrame(ttff_dict).transpose()
+        ttff_data_df = ttff_data_df[[
+            'ttff_loop', 'ttff_sec', 'ttff_pe', 'ttff_haccu'
+        ]]
+        try:
+            df_ttff_ffpe = merge(df_ttff_ffpe, ttff_data_df, left_on='loop', right_on='ttff_loop')
+        except: # pylint: disable=bare-except
+            self.log.warning("Can't merge ttff_data and df.")
+        ttff_data_df.to_json(gps_log_path + '/gps_log_ttff_data.json',
+                             orient='table',
+                             index=False)
+        df_ttff_ffpe.to_json(gps_log_path + '/gps_log.json', orient='table', index=False)
         result = gutils.check_ttff_data(self.dut,
                                         ttff_data,
                                         ttff_mode=test_type.command,
                                         criteria=test_type.criteria)
         if not result:
-            raise signals.TestFailure('%s TTFF fails to reach '
-                                      'designated criteria' % test_type.command)
+            raise signals.TestFailure(
+                f'{test_type.command} TTFF fails to reach '
+                'designated criteria')
         return ttff_data
 
     def verify_pe(self, mode):
@@ -285,7 +363,7 @@
         }
 
         if mode not in ffpe_types:
-            raise signals.TestError('Unrecognized mode %s' % mode)
+            raise signals.TestError(f'Unrecognized mode {mode}')
         test_type = ffpe_types.get(mode)
 
         ttff_data = self.get_and_verify_ttff(mode)
@@ -294,8 +372,9 @@
                                       ttff_mode=test_type.command,
                                       pe_criteria=test_type.pecriteria)
         if not result:
-            raise signals.TestFailure('%s TTFF fails to reach '
-                                      'designated criteria' % test_type.command)
+            raise signals.TestFailure(
+                f'{test_type.command} TTFF fails to reach '
+                'designated criteria')
         return ttff_data
 
     def clear_gps_log(self):
@@ -303,9 +382,77 @@
         Delete the existing GPS GTW Log from DUT.
 
         """
-        self.dut.adb.shell("rm -rf {}".format(DEVICE_GPSLOG_FOLDER))
+        self.dut.adb.shell(f'rm -rf {DEVICE_GPSLOG_FOLDER}')
 
-    def gnss_ttff_ffpe(self, mode, sub_context_path=''):
+    def start_dut_gnss_log(self):
+        """Start GNSS chip log according to different diag_option"""
+        # Start GNSS chip log
+        if self.diag_option == "QCOM":
+            diaglog.start_diagmdlog_background(self.dut, maskfile=self.maskfile)
+        else:
+            gutils.start_pixel_logger(self.dut)
+
+    def stop_and_pull_dut_gnss_log(self, gnss_vendor_log_path=None):
+        """
+        Stop DUT GNSS logger and pull log into local PC dir
+            Arg:
+                gnss_vendor_log_path: gnss log path directory.
+                    Type, str.
+                    Default, None
+        """
+        if not gnss_vendor_log_path:
+            gnss_vendor_log_path = self.gnss_log_path
+        if self.diag_option == "QCOM":
+            diaglog.stop_background_diagmdlog(self.dut,
+                                              gnss_vendor_log_path,
+                                              keep_logs=False)
+        else:
+            gutils.stop_pixel_logger(self.dut)
+            self.log.info('Getting Pixel BCM Log!')
+            diaglog.get_pixellogger_bcm_log(self.dut,
+                                            gnss_vendor_log_path,
+                                            keep_logs=False)
+
+    def start_gnss_and_wait(self, wait=60):
+        """
+        The process of enable gnss and spend the wait time for GNSS to
+        gather enoung information that make sure the stability of testing.
+
+        Args:
+            wait: wait time between power sweep.
+                Type, int.
+                Default, 60.
+        """
+        # Create log path for waiting section logs of GPStool.
+        gnss_wait_log_dir = os.path.join(self.gnss_log_path, 'GNSS_wait')
+
+        # Enable GNSS to receive satellites' signals for "wait_between_pwr" seconds.
+        self.log.info('Enable GNSS for searching satellites')
+        gutils.start_gnss_by_gtw_gpstool(self.dut, state=True)
+        self.log.info(f'Wait for {wait} seconds')
+        time.sleep(wait)
+
+        # Stop GNSS and pull the logs.
+        gutils.start_gnss_by_gtw_gpstool(self.dut, state=False)
+        diaglog.get_gpstool_logs(self.dut, gnss_wait_log_dir, False)
+
+    def exe_eecoexer_loop_cmd(self, cmd_list=None):
+        """
+        Function for execute EECoexer command list
+            Args:
+                cmd_list: a list of EECoexer function command.
+                Type, list.
+        """
+        if cmd_list:
+            for cmd in cmd_list:
+                self.log.info('Execute EEcoexer Command: {}'.format(cmd))
+                gutils.execute_eecoexer_function(self.dut, cmd)
+
+    def gnss_ttff_ffpe(self,
+                       mode,
+                       sub_context_path='',
+                       coex_cmd='',
+                       stop_coex_cmd=''):
         """
         Base ttff and ffpe function
             Args:
@@ -317,16 +464,24 @@
         full_output_path = get_current_context().get_full_output_path()
         self.gnss_log_path = os.path.join(full_output_path, sub_context_path)
         os.makedirs(self.gnss_log_path, exist_ok=True)
-        self.log.debug('Create log path: {}'.format(self.gnss_log_path))
+        self.log.debug(f'Create log path: {self.gnss_log_path}')
 
         # Start and set GNSS simulator
-        self.start_and_set_gnss_simulator_power()
+        self.start_set_gnss_power()
 
         # Start GNSS chip log
-        if self.diag_option == "QCOM":
-            diaglog.start_diagmdlog_background(self.dut, maskfile=self.maskfile)
+        self.start_dut_gnss_log()
+
+        # Wait for acquiring almanac
+        if mode != 'cs':
+            wait_time = 900
         else:
-            gutils.start_pixel_logger(self.dut)
+            wait_time = 3
+        self.start_gnss_and_wait(wait=wait_time)
+
+        # Start Coex if available
+        if coex_cmd and stop_coex_cmd:
+            self.exe_eecoexer_loop_cmd(coex_cmd)
 
         # Start verifying TTFF and FFPE
         self.verify_pe(mode)
@@ -337,13 +492,8 @@
         os.makedirs(gnss_vendor_log_path, exist_ok=True)
 
         # Stop GNSS chip log and pull the logs to local file system
-        if self.diag_option == "QCOM":
-            diaglog.stop_background_diagmdlog(self.dut,
-                                              gnss_vendor_log_path,
-                                              keep_logs=False)
-        else:
-            gutils.stop_pixel_logger(self.dut)
-            self.log.info('Getting Pixel BCM Log!')
-            diaglog.get_pixellogger_bcm_log(self.dut,
-                                            gnss_vendor_log_path,
-                                            keep_logs=False)
+        self.stop_and_pull_dut_gnss_log(gnss_vendor_log_path)
+
+        # Stop Coex if available
+        if coex_cmd and stop_coex_cmd:
+            self.exe_eecoexer_loop_cmd(stop_coex_cmd)
diff --git a/acts_tests/acts_contrib/test_utils/gnss/gnss_constant.py b/acts_tests/acts_contrib/test_utils/gnss/gnss_constant.py
new file mode 100644
index 0000000..c7835ac
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/gnss/gnss_constant.py
@@ -0,0 +1 @@
+TTFF_MODE = {"cs": "Cold Start", "ws": "Warm Start", "hs": "Hot Start", "csa": "CSWith Assist"}
diff --git a/acts_tests/acts_contrib/test_utils/gnss/gnss_measurement.py b/acts_tests/acts_contrib/test_utils/gnss/gnss_measurement.py
new file mode 100644
index 0000000..a9f9579
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/gnss/gnss_measurement.py
@@ -0,0 +1,208 @@
+import os
+import pathlib
+import re
+import shutil
+import tempfile
+from collections import defaultdict
+
+
+class AdrInfo:
+    """Represent one ADR value
+    An ADR value is a decimal number range from 0 - 31
+
+    How to parse the ADR value:
+        First, transform the decimal number to binary then we will get a 5 bit number
+        The meaning of each bit is as follow:
+                 0                  0               0         0       0
+        HalfCycleReported   HalfCycleResolved   CycleSlip   Reset   Valid
+    Special rule:
+        For an ADR value in binary fits the pattern: * * 0 0 1, we call it a usable ADR
+    More insight of ADR value:
+        go/adrstates
+
+    Attributes:
+        is_valid: (bool)
+        is_reset: (bool)
+        is_cycle_slip: (bool)
+        is_half_cycle_resolved: (bool)
+        is_half_cycle_reported: (bool)
+        is_usable: (bool)
+    """
+    def __init__(self, adr_value: int, count: int):
+        src = bin(int(adr_value))
+        self._valid = int(src[-1])
+        self._reset = int(src[-2])
+        self._cycle_slip = int(src[-3])
+        self._half_cycle_resolved = int(src[-4])
+        self._half_cycle_reported = int(src[-5])
+        self.count = count
+
+    @property
+    def is_usable(self):
+        return self.is_valid and not self.is_reset and not self.is_cycle_slip
+
+    @property
+    def is_valid(self):
+        return bool(self._valid)
+
+    @property
+    def is_reset(self):
+        return bool(self._reset)
+
+    @property
+    def is_cycle_slip(self):
+        return bool(self._cycle_slip)
+
+    @property
+    def is_half_cycle_resolved(self):
+        return bool(self._half_cycle_resolved)
+
+    @property
+    def is_half_cycle_reported(self):
+        return bool(self._half_cycle_reported)
+
+
+class AdrStatistic:
+    """Represent the ADR statistic
+
+    Attributes:
+        usable_count: (int)
+        valid_count: (int)
+        reset_count: (int)
+        cycle_slip_count: (int)
+        half_cycle_resolved_count: (int)
+        half_cycle_reported_count: (int)
+        total_count: (int)
+        usable_rate: (float)
+            usable_count / total_count
+        valid_rate: (float)
+            valid_count / total_count
+    """
+    def __init__(self):
+        self.usable_count = 0
+        self.valid_count = 0
+        self.reset_count = 0
+        self.cycle_slip_count = 0
+        self.half_cycle_resolved_count = 0
+        self.half_cycle_reported_count = 0
+        self.total_count = 0
+
+    @property
+    def usable_rate(self):
+        denominator = max(1, self.total_count)
+        result = self.usable_count / denominator
+        return round(result, 3)
+
+    @property
+    def valid_rate(self):
+        denominator = max(1, self.total_count)
+        result = self.valid_count / denominator
+        return round(result, 3)
+
+    def add_adr_info(self, adr_info: AdrInfo):
+        """Add ADR info record to increase the statistic
+
+        Args:
+            adr_info: AdrInfo object
+        """
+        if adr_info.is_valid:
+            self.valid_count += adr_info.count
+        if adr_info.is_reset:
+            self.reset_count += adr_info.count
+        if adr_info.is_cycle_slip:
+            self.cycle_slip_count += adr_info.count
+        if adr_info.is_half_cycle_resolved:
+            self.half_cycle_resolved_count += adr_info.count
+        if adr_info.is_half_cycle_reported:
+            self.half_cycle_reported_count += adr_info.count
+        if adr_info.is_usable:
+            self.usable_count += adr_info.count
+        self.total_count += adr_info.count
+
+
+
+class GnssMeasurement:
+    """Represent the content of measurement file generated by gps tool"""
+
+    FILE_PATTERN = "/storage/emulated/0/Android/data/com.android.gpstool/files/MEAS*.txt"
+
+    def __init__(self, ad):
+        self.ad = ad
+
+    def _generate_local_temp_path(self, file_name="file.txt"):
+        """Generate a file path for temporarily usage
+
+        Returns:
+            string: local file path
+        """
+        folder = tempfile.mkdtemp()
+        file_path = os.path.join(folder, file_name)
+        return file_path
+
+    def _get_latest_measurement_file_path(self):
+        """Get the latest measurement file path on device
+
+        Returns:
+            string: file path on device
+        """
+        command = f"ls -tr {self.FILE_PATTERN} | tail -1"
+        result = self.ad.adb.shell(command)
+        return result
+
+    def get_latest_measurement_file(self):
+        """Pull the latest measurement file from device to local
+
+        Returns:
+            string: local file path to the measurement file
+
+        Raise:
+            FileNotFoundError: can't get measurement file from device
+        """
+        self.ad.log.info("Get measurement file from device")
+        dest = self._generate_local_temp_path(file_name="measurement.txt")
+        src = self._get_latest_measurement_file_path()
+        if not src:
+            raise FileNotFoundError(f"Can not find measurement file: pattern {self.FILE_PATTERN}")
+        self.ad.pull_files(src, dest)
+        return dest
+
+    def _get_adr_src_value(self):
+        """Get ADR value from measurement file
+
+        Returns:
+            dict: {<ADR_value>: count, <ADR_value>: count...}
+        """
+        try:
+            file_path = self.get_latest_measurement_file()
+            adr_src = defaultdict(int)
+            adr_src_regex = re.compile("=\s(\d*)")
+            with open(file_path) as f:
+                for line in f:
+                    if "AccumulatedDeltaRangeState" in line:
+                        value = re.search(adr_src_regex, line)
+                        if not value:
+                            self.ad.log.warn("Can't get ADR value %s" % line)
+                            continue
+                        key = value.group(1)
+                        adr_src[key] += 1
+            return adr_src
+        finally:
+            folder = pathlib.PurePosixPath(file_path).parent
+            shutil.rmtree(folder, ignore_errors=True)
+
+    def get_adr_static(self):
+        """Get ADR statistic
+
+        Summarize ADR value from measurement file
+
+        Returns:
+            AdrStatistic object
+        """
+        self.ad.log.info("Get ADR statistic")
+        adr_src = self._get_adr_src_value()
+        adr_static = AdrStatistic()
+        for key, value in adr_src.items():
+            self.ad.log.debug("ADR value: %s - count: %s" % (key, value))
+            adr_info = AdrInfo(key, value)
+            adr_static.add_adr_info(adr_info)
+        return adr_static
diff --git a/acts_tests/acts_contrib/test_utils/gnss/gnss_test_utils.py b/acts_tests/acts_contrib/test_utils/gnss/gnss_test_utils.py
index 5efa817..ec9ba61 100644
--- a/acts_tests/acts_contrib/test_utils/gnss/gnss_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/gnss/gnss_test_utils.py
@@ -13,31 +13,39 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-
 import time
 import re
 import os
+import pathlib
 import math
 import shutil
 import fnmatch
 import posixpath
+import subprocess
 import tempfile
-import zipfile
+from retry import retry
 from collections import namedtuple
 from datetime import datetime
 from xml.etree import ElementTree
+from contextlib import contextmanager
+from statistics import median
 
 from acts import utils
 from acts import asserts
 from acts import signals
 from acts.libs.proc import job
+from acts.controllers.adb_lib.error import AdbCommandError
 from acts.controllers.android_device import list_adb_devices
 from acts.controllers.android_device import list_fastboot_devices
 from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
 from acts.controllers.android_device import SL4A_APK_NAME
+from acts_contrib.test_utils.gnss.gnss_measurement import GnssMeasurement
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 from acts_contrib.test_utils.tel import tel_logging_utils as tlutils
 from acts_contrib.test_utils.tel import tel_test_utils as tutils
+from acts_contrib.test_utils.gnss import gnssstatus_utils
+from acts_contrib.test_utils.gnss import gnss_constant
+from acts_contrib.test_utils.gnss import supl
 from acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationCommandBuilder
 from acts_contrib.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationTestCommandBuilder
 from acts.utils import get_current_epoch_time
@@ -46,6 +54,8 @@
 from acts_contrib.test_utils.gnss.gnss_defines import BCM_NVME_STO_PATH
 
 WifiEnums = wutils.WifiEnums
+FIRST_FIXED_MAX_WAITING_TIME = 60
+UPLOAD_TO_SPONGE_PREFIX = "TestResult "
 PULL_TIMEOUT = 300
 GNSSSTATUS_LOG_PATH = (
     "/storage/emulated/0/Android/data/com.android.gpstool/files/")
@@ -54,7 +64,7 @@
     "TTFF_REPORT", "utc_time ttff_loop ttff_sec ttff_pe ttff_ant_cn "
                    "ttff_base_cn ttff_haccu")
 TRACK_REPORT = namedtuple(
-    "TRACK_REPORT", "l5flag pe ant_top4cn ant_cn base_top4cn base_cn")
+    "TRACK_REPORT", "l5flag pe ant_top4cn ant_cn base_top4cn base_cn device_time report_time")
 LOCAL_PROP_FILE_CONTENTS = """\
 log.tag.LocationManagerService=VERBOSE
 log.tag.GnssLocationProvider=VERBOSE
@@ -70,6 +80,11 @@
 log.tag.GnssPsdsDownloader=VERBOSE
 log.tag.Gnss=VERBOSE
 log.tag.GnssConfiguration=VERBOSE"""
+LOCAL_PROP_FILE_CONTENTS_FOR_WEARABLE = """\
+log.tag.ImsPhone=VERBOSE
+log.tag.GsmCdmaPhone=VERBOSE
+log.tag.Phone=VERBOSE
+log.tag.GCoreFlp=VERBOSE"""
 TEST_PACKAGE_NAME = "com.google.android.apps.maps"
 LOCATION_PERMISSIONS = [
     "android.permission.ACCESS_FINE_LOCATION",
@@ -99,6 +114,7 @@
 XTRA_SERVER_2="http://"
 XTRA_SERVER_3="http://"
 """
+_BRCM_DUTY_CYCLE_PATTERN = re.compile(r".*PGLOR,\d+,STA.*")
 
 
 class GnssTestUtilsError(Exception):
@@ -129,7 +145,8 @@
         ad: An AndroidDevice object.
     """
     ad.log.info("Reboot device to make changes take effect.")
-    ad.reboot()
+    # TODO(diegowchung): remove the timeout setting after p23 back to normal
+    ad.reboot(timeout=600)
     ad.unlock_screen(password=None)
     if not is_mobile_data_on(ad):
         set_mobile_data(ad, True)
@@ -149,7 +166,11 @@
     else:
         ad.adb.shell("echo LogEnabled=true >> /data/vendor/gps/libgps.conf")
         ad.adb.shell("chown gps.system /data/vendor/gps/libgps.conf")
-    ad.adb.shell("echo %r >> /data/local.prop" % LOCAL_PROP_FILE_CONTENTS)
+    if is_device_wearable(ad):
+       PROP_CONTENTS = LOCAL_PROP_FILE_CONTENTS + LOCAL_PROP_FILE_CONTENTS_FOR_WEARABLE
+    else:
+        PROP_CONTENTS = LOCAL_PROP_FILE_CONTENTS
+    ad.adb.shell("echo %r >> /data/local.prop" % PROP_CONTENTS)
     ad.adb.shell("chmod 644 /data/local.prop")
     ad.adb.shell("setprop persist.logd.logpersistd.size 20000")
     ad.adb.shell("setprop persist.logd.size 16777216")
@@ -221,12 +242,6 @@
     remount_device(ad)
     ad.log.info("Enable SUPL mode.")
     ad.adb.shell("echo -e '\nSUPL_MODE=1' >> /etc/gps_debug.conf")
-    if is_device_wearable(ad):
-        lto_mode_wearable(ad, True)
-    elif not check_chipset_vendor_by_qualcomm(ad):
-        lto_mode(ad, True)
-    else:
-        reboot(ad)
 
 
 def disable_supl_mode(ad):
@@ -238,16 +253,33 @@
     remount_device(ad)
     ad.log.info("Disable SUPL mode.")
     ad.adb.shell("echo -e '\nSUPL_MODE=0' >> /etc/gps_debug.conf")
+    if not check_chipset_vendor_by_qualcomm(ad):
+        supl.set_supl_over_wifi_state(ad, False)
+
+
+def enable_vendor_orbit_assistance_data(ad):
+    """Enable vendor assistance features.
+        For Qualcomm: Enable XTRA
+        For Broadcom: Enable LTO
+
+    Args:
+        ad: An AndroidDevice object.
+    """
+    ad.root_adb()
     if is_device_wearable(ad):
         lto_mode_wearable(ad, True)
-    elif not check_chipset_vendor_by_qualcomm(ad):
-        lto_mode(ad, True)
-    else:
+    elif check_chipset_vendor_by_qualcomm(ad):
+        disable_xtra_throttle(ad)
         reboot(ad)
+    else:
+        lto_mode(ad, True)
 
 
-def kill_xtra_daemon(ad):
-    """Kill XTRA daemon to test SUPL only test item.
+def disable_vendor_orbit_assistance_data(ad):
+    """Disable vendor assistance features.
+
+    For Qualcomm: disable XTRA
+    For Broadcom: disable LTO
 
     Args:
         ad: An AndroidDevice object.
@@ -256,11 +288,41 @@
     if is_device_wearable(ad):
         lto_mode_wearable(ad, False)
     elif check_chipset_vendor_by_qualcomm(ad):
-        ad.log.info("Disable XTRA-daemon until next reboot.")
-        ad.adb.shell("killall xtra-daemon", ignore_status=True)
+        disable_qualcomm_orbit_assistance_data(ad)
     else:
         lto_mode(ad, False)
 
+def gla_mode(ad, state: bool):
+    """Enable or disable Google Location Accuracy feature.
+
+    Args:
+        ad: An AndroidDevice object.
+        state: True to enable GLA, False to disable GLA.
+    """
+    ad.root_adb()
+    if state:
+        ad.adb.shell('settings put global assisted_gps_enabled 1')
+        ad.log.info("Modify current GLA Mode to MS_BASED mode")
+    else:
+        ad.adb.shell('settings put global assisted_gps_enabled 0')
+        ad.log.info("Modify current GLA Mode to standalone mode")
+
+    out = int(ad.adb.shell("settings get global assisted_gps_enabled"))
+    if out == 1:
+        ad.log.info("GLA is enabled, MS_BASED mode")
+    else:
+        ad.log.info("GLA is disabled, standalone mode")
+
+
+def disable_qualcomm_orbit_assistance_data(ad):
+    """Disable assiatance features for Qualcomm project.
+
+    Args:
+        ad: An AndroidDevice object.
+    """
+    ad.log.info("Disable XTRA-daemon until next reboot.")
+    ad.adb.shell("killall xtra-daemon", ignore_status=True)
+
 
 def disable_private_dns_mode(ad):
     """Due to b/118365122, it's better to disable private DNS mode while
@@ -282,26 +344,17 @@
     Args:
         ad: An AndroidDevice object.
     """
+    check_location_service(ad)
     enable_gnss_verbose_logging(ad)
-    enable_compact_and_particle_fusion_log(ad)
     prepare_gps_overlay(ad)
-    if check_chipset_vendor_by_qualcomm(ad):
-        disable_xtra_throttle(ad)
-    enable_supl_mode(ad)
-    if is_device_wearable(ad):
-        ad.adb.shell("settings put global stay_on_while_plugged_in 7")
-    else:
-        ad.adb.shell("settings put system screen_off_timeout 1800000")
-    wutils.wifi_toggle_state(ad, False)
+    set_screen_always_on(ad)
     ad.log.info("Setting Bluetooth state to False")
     ad.droid.bluetoothToggleState(False)
-    check_location_service(ad)
     set_wifi_and_bt_scanning(ad, True)
     disable_private_dns_mode(ad)
-    reboot(ad)
     init_gtw_gpstool(ad)
-    if not is_mobile_data_on(ad):
-        set_mobile_data(ad, True)
+    if is_device_wearable(ad):
+        disable_battery_defend(ad)
 
 
 def prepare_gps_overlay(ad):
@@ -368,8 +421,14 @@
     ad.ed.clear_all_events()
     wutils.reset_wifi(ad)
     wutils.start_wifi_connection_scan_and_ensure_network_found(ad, SSID)
-    wutils.wifi_connect(ad, network, num_of_tries=5)
-
+    for i in range(5):
+        wutils.wifi_connect(ad, network, check_connectivity=False)
+        # Validates wifi connection with ping_gateway=False to avoid issue like
+        # b/254913994.
+        if wutils.validate_connection(ad, ping_gateway=False):
+            ad.log.info("WiFi connection is validated")
+            return
+    raise signals.TestError("Failed to connect WiFi")
 
 def set_wifi_and_bt_scanning(ad, state=True):
     """Set Wi-Fi and Bluetooth scanning on/off in Settings -> Location
@@ -406,6 +465,22 @@
         raise signals.TestError("Failed to turn Location on")
 
 
+def delete_device_folder(ad, folder):
+    ad.log.info("Folder to be deleted: %s" % folder)
+    folder_contents = ad.adb.shell(f"ls {folder}", ignore_status=True)
+    ad.log.debug("Contents to be deleted: %s" % folder_contents)
+    ad.adb.shell("rm -rf %s" % folder, ignore_status=True)
+
+
+def remove_pixel_logger_folder(ad):
+    if check_chipset_vendor_by_qualcomm(ad):
+        folder = "/sdcard/Android/data/com.android.pixellogger/files/logs/diag_logs"
+    else:
+        folder = "/sdcard/Android/data/com.android.pixellogger/files/logs/gps/"
+
+    delete_device_folder(ad, folder)
+
+
 def clear_logd_gnss_qxdm_log(ad):
     """Clear /data/misc/logd,
     /storage/emulated/0/Android/data/com.android.gpstool/files and
@@ -416,20 +491,21 @@
     """
     remount_device(ad)
     ad.log.info("Clear Logd, GNSS and PixelLogger Log from previous test item.")
-    ad.adb.shell("rm -rf /data/misc/logd", ignore_status=True)
+    folders_should_be_removed = ["/data/misc/logd"]
     ad.adb.shell(
         'find %s -name "*.txt" -type f -delete' % GNSSSTATUS_LOG_PATH,
         ignore_status=True)
     if check_chipset_vendor_by_qualcomm(ad):
-        diag_logs = (
-            "/sdcard/Android/data/com.android.pixellogger/files/logs/diag_logs")
-        ad.adb.shell("rm -rf %s" % diag_logs, ignore_status=True)
         output_path = posixpath.join(DEFAULT_QXDM_LOG_PATH, "logs")
+        folders_should_be_removed += [output_path]
     else:
-        output_path = ("/sdcard/Android/data/com.android.pixellogger/files"
-                       "/logs/gps/")
-    ad.adb.shell("rm -rf %s" % output_path, ignore_status=True)
-    reboot(ad)
+        always_on_logger_log_path = ("/data/vendor/gps/logs")
+        folders_should_be_removed += [always_on_logger_log_path]
+    for folder in folders_should_be_removed:
+        delete_device_folder(ad, folder)
+    remove_pixel_logger_folder(ad)
+    if not is_device_wearable(ad):
+        reboot(ad)
 
 
 def get_gnss_qxdm_log(ad, qdb_path=None):
@@ -589,14 +665,13 @@
             ad.log.info("XTRA downloaded and injected successfully.")
             return True
         ad.log.error("XTRA downloaded FAIL.")
-    elif is_device_wearable(ad):
-        lto_results = ad.adb.shell("ls -al /data/vendor/gps/lto*")
-        if "lto2.dat" in lto_results:
-            ad.log.info("LTO downloaded and injected successfully.")
-            return True
     else:
-        lto_results = ad.search_logcat("GnssPsdsAidl: injectPsdsData: "
-                                       "psdsType: 1", begin_time)
+        if is_device_wearable(ad):
+            lto_results = ad.search_logcat("GnssLocationProvider: "
+                                           "calling native_inject_psds_data", begin_time)
+        else:
+            lto_results = ad.search_logcat("GnssPsdsAidl: injectPsdsData: "
+                                           "psdsType: 1", begin_time)
         if lto_results:
             ad.log.debug("%s" % lto_results[-1]["log_message"])
             ad.log.info("LTO downloaded and injected successfully.")
@@ -776,7 +851,7 @@
 
 def start_gnss_by_gtw_gpstool(ad,
                               state,
-                              type="gnss",
+                              api_type="gnss",
                               bgdisplay=False,
                               freq=0,
                               lowpower=False,
@@ -786,7 +861,7 @@
     Args:
         ad: An AndroidDevice object.
         state: True to start GNSS. False to Stop GNSS.
-        type: Different API for location fix. Use gnss/flp/nmea
+        api_type: Different API for location fix. Use gnss/flp/nmea
         bgdisplay: true to run GTW when Display off. false to not run GTW when
           Display off.
         freq: An integer to set location update frequency.
@@ -795,36 +870,42 @@
     """
     cmd = "am start -S -n com.android.gpstool/.GPSTool --es mode gps"
     if not state:
-        ad.log.info("Stop %s on GTW_GPSTool." % type)
+        ad.log.info("Stop %s on GTW_GPSTool." % api_type)
         cmd = "am broadcast -a com.android.gpstool.stop_gps_action"
     else:
         options = ("--es type {} --ei freq {} --ez BG {} --ez meas {} --ez "
-                   "lowpower {}").format(type, freq, bgdisplay, meas, lowpower)
+                   "lowpower {}").format(api_type, freq, bgdisplay, meas, lowpower)
         cmd = cmd + " " + options
-    ad.adb.shell(cmd)
+    ad.adb.shell(cmd, ignore_status=True, timeout = 300)
     time.sleep(3)
 
 
 def process_gnss_by_gtw_gpstool(ad,
                                 criteria,
-                                type="gnss",
+                                api_type="gnss",
                                 clear_data=True,
-                                meas_flag=False):
+                                meas_flag=False,
+                                freq=0,
+                                bg_display=False):
     """Launch GTW GPSTool and Clear all GNSS aiding data
        Start GNSS tracking on GTW_GPSTool.
 
     Args:
         ad: An AndroidDevice object.
         criteria: Criteria for current test item.
-        type: Different API for location fix. Use gnss/flp/nmea
+        api_type: Different API for location fix. Use gnss/flp/nmea
         clear_data: True to clear GNSS aiding data. False is not to. Default
         set to True.
         meas_flag: True to enable GnssMeasurement. False is not to. Default
         set to False.
+        freq: An integer to set location update frequency. Default set to 0.
+        bg_display: To enable GPS tool bg display or not
 
     Returns:
-        True: First fix TTFF are within criteria.
-        False: First fix TTFF exceed criteria.
+        First fix datetime obj
+
+    Raises:
+        signals.TestFailure: when first fixed is over criteria or not even get first fixed
     """
     retries = 3
     for i in range(retries):
@@ -836,27 +917,28 @@
         begin_time = get_current_epoch_time()
         if clear_data:
             clear_aiding_data_by_gtw_gpstool(ad)
-        ad.log.info("Start %s on GTW_GPSTool - attempt %d" % (type.upper(),
+        ad.log.info("Start %s on GTW_GPSTool - attempt %d" % (api_type.upper(),
                                                               i+1))
-        start_gnss_by_gtw_gpstool(ad, state=True, type=type, meas=meas_flag)
+        start_gnss_by_gtw_gpstool(ad, state=True, api_type=api_type, meas=meas_flag, freq=freq,
+                                  bgdisplay=bg_display)
         for _ in range(10 + criteria):
             logcat_results = ad.search_logcat("First fixed", begin_time)
             if logcat_results:
                 ad.log.debug(logcat_results[-1]["log_message"])
                 first_fixed = int(logcat_results[-1]["log_message"].split()[-1])
                 ad.log.info("%s First fixed = %.3f seconds" %
-                            (type.upper(), first_fixed/1000))
+                            (api_type.upper(), first_fixed/1000))
                 if (first_fixed/1000) <= criteria:
-                    return True
-                start_gnss_by_gtw_gpstool(ad, state=False, type=type)
+                    return logcat_results[-1]["datetime_obj"]
+                start_gnss_by_gtw_gpstool(ad, state=False, api_type=api_type)
                 raise signals.TestFailure("Fail to get %s location fixed "
                                           "within %d seconds criteria."
-                                          % (type.upper(), criteria))
+                                          % (api_type.upper(), criteria))
             time.sleep(1)
         check_current_focus_app(ad)
-        start_gnss_by_gtw_gpstool(ad, state=False, type=type)
+        start_gnss_by_gtw_gpstool(ad, state=False, api_type=api_type)
     raise signals.TestFailure("Fail to get %s location fixed within %d "
-                              "attempts." % (type.upper(), retries))
+                              "attempts." % (api_type.upper(), retries))
 
 
 def start_ttff_by_gtw_gpstool(ad,
@@ -866,7 +948,8 @@
                               raninterval=False,
                               mininterval=10,
                               maxinterval=40,
-                              hot_warm_sleep=300):
+                              hot_warm_sleep=300,
+                              timeout=60):
     """Identify which TTFF mode for different test items.
 
     Args:
@@ -878,8 +961,12 @@
         mininterval: Minimum value of random interval pool. The unit is second.
         maxinterval: Maximum value of random interval pool. The unit is second.
         hot_warm_sleep: Wait time for acquiring Almanac.
+        timeout: TTFF time out. The unit is second.
+    Returns:
+        latest_start_time: (Datetime) the start time of latest successful TTFF
     """
     begin_time = get_current_epoch_time()
+    ad.log.debug("[start_ttff] Search logcat start time: %s" % begin_time)
     if (ttff_mode == "hs" or ttff_mode == "ws") and not aid_data:
         ad.log.info("Wait {} seconds to start TTFF {}...".format(
             hot_warm_sleep, ttff_mode.upper()))
@@ -891,16 +978,32 @@
         ad.log.info("Start TTFF CSWith Assist...")
         time.sleep(3)
     for i in range(1, 4):
-        ad.adb.shell("am broadcast -a com.android.gpstool.ttff_action "
-                     "--es ttff {} --es cycle {}  --ez raninterval {} "
-                     "--ei mininterval {} --ei maxinterval {}".format(
+        try:
+            ad.log.info(f"Before sending TTFF gms version is {get_gms_version(ad)}")
+            ad.adb.shell("am broadcast -a com.android.gpstool.ttff_action "
+                         "--es ttff {} --es cycle {}  --ez raninterval {} "
+                         "--ei mininterval {} --ei maxinterval {}".format(
                          ttff_mode, iteration, raninterval, mininterval,
                          maxinterval))
+        except job.TimeoutError:
+            # If this is the last retry and we still get timeout error, raises the timeoutError.
+            if i == 3:
+                raise
+            # Currently we encounter lots of timeout issue in Qualcomm devices. But so far we don't
+            # know the root cause yet. In order to continue the test, we ignore the timeout for
+            # retry.
+            ad.log.warn("Send TTFF command timeout.")
+            ad.log.info(f"Current gms version is {get_gms_version(ad)}")
+            # Wait 2 second to retry
+            time.sleep(2)
+            continue
         time.sleep(1)
-        if ad.search_logcat("act=com.android.gpstool.start_test_action",
-                            begin_time):
+        result = ad.search_logcat("act=com.android.gpstool.start_test_action", begin_time)
+        if result:
+            ad.log.debug("TTFF start log %s" % result)
+            latest_start_time = max(list(map(lambda x: x['datetime_obj'], result)))
             ad.log.info("Send TTFF start_test_action successfully.")
-            break
+            return latest_start_time
     else:
         check_current_focus_app(ad)
         raise signals.TestError("Fail to send TTFF start_test_action.")
@@ -908,31 +1011,73 @@
 
 def gnss_tracking_via_gtw_gpstool(ad,
                                   criteria,
-                                  type="gnss",
+                                  api_type="gnss",
                                   testtime=60,
-                                  meas_flag=False):
+                                  meas_flag=False,
+                                  freq=0,
+                                  is_screen_off=False):
     """Start GNSS/FLP tracking tests for input testtime on GTW_GPSTool.
 
     Args:
         ad: An AndroidDevice object.
         criteria: Criteria for current TTFF.
-        type: Different API for location fix. Use gnss/flp/nmea
+        api_type: Different API for location fix. Use gnss/flp/nmea
         testtime: Tracking test time for minutes. Default set to 60 minutes.
         meas_flag: True to enable GnssMeasurement. False is not to. Default
         set to False.
+        freq: An integer to set location update frequency. Default set to 0.
+        is_screen_off: whether to turn off during tracking
     """
     process_gnss_by_gtw_gpstool(
-        ad, criteria=criteria, type=type, meas_flag=meas_flag)
-    ad.log.info("Start %s tracking test for %d minutes" % (type.upper(),
+        ad, criteria=criteria, api_type=api_type, meas_flag=meas_flag, freq=freq,
+        bg_display=is_screen_off)
+    ad.log.info("Start %s tracking test for %d minutes" % (api_type.upper(),
                                                            testtime))
     begin_time = get_current_epoch_time()
+    with set_screen_status(ad, off=is_screen_off):
+        wait_n_mins_for_gnss_tracking(ad, begin_time, testtime, api_type)
+        ad.log.info("Successfully tested for %d minutes" % testtime)
+    start_gnss_by_gtw_gpstool(ad, state=False, api_type=api_type)
+
+
+def wait_n_mins_for_gnss_tracking(ad, begin_time, testtime, api_type="gnss",
+                                  ignore_hal_crash=False):
+    """Waits for GNSS tracking to finish and detect GNSS crash during the waiting time.
+
+    Args:
+        ad: An AndroidDevice object.
+        begin_time: The start time of tracking.
+        api_type: Different API for location fix. Use gnss/flp/nmea
+        testtime: Tracking test time for minutes.
+        ignore_hal_crash: To ignore HAL crash error no not.
+    """
     while get_current_epoch_time() - begin_time < testtime * 60 * 1000:
-        detect_crash_during_tracking(ad, begin_time, type)
-    ad.log.info("Successfully tested for %d minutes" % testtime)
-    start_gnss_by_gtw_gpstool(ad, state=False, type=type)
+        detect_crash_during_tracking(ad, begin_time, api_type, ignore_hal_crash)
+        # add sleep here to avoid too many request and cause device not responding
+        time.sleep(1)
 
+def run_ttff_via_gtw_gpstool(ad, mode, criteria, test_cycle, true_location):
+    """Run GNSS TTFF test with selected mode and parse the results.
 
-def parse_gtw_gpstool_log(ad, true_position, type="gnss"):
+    Args:
+        mode: "cs", "ws" or "hs"
+        criteria: Criteria for the TTFF.
+
+    Returns:
+        ttff_data: A dict of all TTFF data.
+    """
+    # Before running TTFF, we will run tracking and try to get first fixed.
+    # But the TTFF before TTFF doesn't apply to any criteria, so we set a maximum value.
+    process_gnss_by_gtw_gpstool(ad, criteria=FIRST_FIXED_MAX_WAITING_TIME)
+    ttff_start_time = start_ttff_by_gtw_gpstool(ad, mode, test_cycle)
+    ttff_data = process_ttff_by_gtw_gpstool(ad, ttff_start_time, true_location)
+    result = check_ttff_data(ad, ttff_data, gnss_constant.TTFF_MODE.get(mode), criteria)
+    asserts.assert_true(
+        result, "TTFF %s fails to reach designated criteria: %d "
+                "seconds." % (gnss_constant.TTFF_MODE.get(mode), criteria))
+    return ttff_data
+
+def parse_gtw_gpstool_log(ad, true_position, api_type="gnss", validate_gnssstatus=False):
     """Process GNSS/FLP API logs from GTW GPSTool and output track_data to
     test_run_info for ACTS plugin to parse and display on MobileHarness as
     Property.
@@ -941,8 +1086,14 @@
         ad: An AndroidDevice object.
         true_position: Coordinate as [latitude, longitude] to calculate
         position error.
-        type: Different API for location fix. Use gnss/flp/nmea
+        api_type: Different API for location fix. Use gnss/flp/nmea
+        validate_gnssstatus: Validate gnssstatus or not
+
+    Returns:
+        A dict of location reported from GPSTool
+            {<utc_time>: TRACK_REPORT, ...}
     """
+    gnssstatus_count = 0
     test_logfile = {}
     track_data = {}
     ant_top4_cn = 0
@@ -952,11 +1103,13 @@
     track_lat = 0
     track_long = 0
     l5flag = "false"
+    gps_datetime_pattern = re.compile("(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}\.\d{0,5})")
+    gps_datetime_format = "%Y/%m/%d %H:%M:%S.%f"
     file_count = int(ad.adb.shell("find %s -type f -iname *.txt | wc -l"
                                   % GNSSSTATUS_LOG_PATH))
     if file_count != 1:
-        ad.log.error("%d API logs exist." % file_count)
-    dir_file = ad.adb.shell("ls %s" % GNSSSTATUS_LOG_PATH).split()
+        ad.log.warn("%d API logs exist." % file_count)
+    dir_file = ad.adb.shell("ls -tr %s" % GNSSSTATUS_LOG_PATH).split()
     for path_key in dir_file:
         if fnmatch.fnmatch(path_key, "*.txt"):
             logpath = posixpath.join(GNSSSTATUS_LOG_PATH, path_key)
@@ -970,7 +1123,20 @@
     if not test_logfile:
         raise signals.TestError("Failed to get test log file in device.")
     lines = ad.adb.shell("cat %s" % test_logfile).split("\n")
+    gnss_svid_container = gnssstatus_utils.GnssSvidContainer()
     for line in lines:
+        if line.startswith('Fix'):
+            try:
+                gnss_status = gnssstatus_utils.GnssStatus(line)
+                gnssstatus_count += 1
+            except gnssstatus_utils.RegexParseException as e:
+                ad.log.warn(e)
+                continue
+
+            gnss_svid_container.add_satellite(gnss_status)
+            if validate_gnssstatus:
+                gnss_status.validate_gnssstatus()
+
         if "Antenna_History Avg Top4" in line:
             ant_top4_cn = float(line.split(":")[-1].strip())
         elif "Antenna_History Avg" in line:
@@ -985,8 +1151,13 @@
             track_lat = float(line.split(":")[-1].strip())
         elif "Longitude" in line:
             track_long = float(line.split(":")[-1].strip())
+        elif "Read:" in line:
+            target = re.search(gps_datetime_pattern, line)
+            device_time = datetime.strptime(target.group(1), gps_datetime_format)
         elif "Time" in line:
-            track_utc = line.split("Time:")[-1].strip()
+            target = re.search(gps_datetime_pattern, line)
+            track_utc = target.group(1)
+            report_time = datetime.strptime(track_utc, gps_datetime_format)
             if track_utc in track_data.keys():
                 continue
             pe = calculate_position_error(track_lat, track_long, true_position)
@@ -995,9 +1166,13 @@
                                                  ant_top4cn=ant_top4_cn,
                                                  ant_cn=ant_cn,
                                                  base_top4cn=base_top4_cn,
-                                                 base_cn=base_cn)
+                                                 base_cn=base_cn,
+                                                 device_time=device_time,
+                                                 report_time=report_time,
+                                                 )
+    ad.log.info("Total %d gnssstatus samples verified" %gnssstatus_count)
     ad.log.debug(track_data)
-    prop_basename = "TestResult %s_tracking_" % type.upper()
+    prop_basename = UPLOAD_TO_SPONGE_PREFIX + f"{api_type.upper()}_tracking_"
     time_list = sorted(track_data.keys())
     l5flag_list = [track_data[key].l5flag for key in time_list]
     pe_list = [float(track_data[key].pe) for key in time_list]
@@ -1016,9 +1191,84 @@
     ad.log.info(prop_basename+"Ant_AvgSignal %.1f" % ant_cn_list[-1])
     ad.log.info(prop_basename+"Base_AvgTop4Signal %.1f" % base_top4cn_list[-1])
     ad.log.info(prop_basename+"Base_AvgSignal %.1f" % base_cn_list[-1])
+    _log_svid_info(gnss_svid_container, prop_basename, ad)
+    return track_data
 
 
-def process_ttff_by_gtw_gpstool(ad, begin_time, true_position, type="gnss"):
+def verify_gps_time_should_be_close_to_device_time(ad, tracking_result):
+    """Check the time gap between GPS time and device time.
+
+    In normal cases, the GPS time should be close to device time. But if GPS week rollover happens,
+    the GPS time may goes back to 20 years ago. In order to capture this issue, we assert the time
+    diff between the GPS time and device time.
+
+    Args:
+        ad: The device under test.
+        tracking_result: The result we get from GNSS tracking.
+    """
+    ad.log.info("Validating GPS/Device time difference")
+    max_time_diff_in_seconds = 2.0
+    exceed_report = []
+    for report in tracking_result.values():
+        time_diff_in_seconds = abs((report.report_time - report.device_time).total_seconds())
+        if time_diff_in_seconds > max_time_diff_in_seconds:
+            message = (f"GPS time: {report.report_time}  Device time: {report.device_time} "
+                       f"diff: {time_diff_in_seconds}")
+            exceed_report.append(message)
+    fail_message = (f"The following items exceed {max_time_diff_in_seconds}s\n" +
+                     "\n".join(exceed_report))
+    asserts.assert_false(exceed_report, msg=fail_message)
+
+
+def validate_location_fix_rate(ad, location_reported, run_time, fix_rate_criteria):
+    """Check location reported count
+
+    The formula is "total_fix_points / (run_time * 60)"
+    When the result is lower than fix_rate_criteria, fail the test case
+
+    Args:
+        ad: AndroidDevice object
+        location_reported: (Enumerate) Contains the reported location
+        run_time: (int) How many minutes do we need to verify
+        fix_rate_criteria: The threshold of the pass criteria
+            if we expect fix rate to be 99%, then fix_rate_criteria should be 0.99
+    """
+    ad.log.info("Validating fix rate")
+    pass_criteria = run_time * 60 * fix_rate_criteria
+    actual_location_count = len(location_reported)
+
+    # The fix rate may exceed 100% occasionally, to standardlize the result
+    # set maximum fix rate to 100%
+    actual_fix_rate = min(1, (actual_location_count / (run_time * 60)))
+    actual_fix_rate_percentage = f"{actual_fix_rate:.0%}"
+
+    log_prefix = UPLOAD_TO_SPONGE_PREFIX + f"FIX_RATE_"
+    ad.log.info("%sresult %s" % (log_prefix, actual_fix_rate_percentage))
+    ad.log.debug("Actual location count %s" % actual_location_count)
+
+    fail_message = (f"Fail to meet criteria. Expect to have at least {pass_criteria} location count"
+                    f" Actual: {actual_location_count}")
+    asserts.assert_true(pass_criteria <= actual_location_count, msg=fail_message)
+
+
+def _log_svid_info(container, log_prefix, ad):
+    """Write GnssSvidContainer svid information into logger
+    Args:
+        container: A GnssSvidContainer object
+        log_prefix:
+            A prefix used to specify the log will be upload to dashboard
+        ad: An AndroidDevice object
+    """
+    for sv_type, svids in container.used_in_fix.items():
+        message = f"{log_prefix}{sv_type} {len(svids)}"
+        ad.log.info(message)
+        ad.log.debug("Satellite used in fix %s ids are: %s", sv_type, svids)
+
+    for sv_type, svids in container.not_used_in_fix.items():
+        ad.log.debug("Satellite not used in fix %s ids are: %s", sv_type, svids)
+
+
+def process_ttff_by_gtw_gpstool(ad, begin_time, true_position, api_type="gnss"):
     """Process TTFF and record results in ttff_data.
 
     Args:
@@ -1026,7 +1276,7 @@
         begin_time: test begin time.
         true_position: Coordinate as [latitude, longitude] to calculate
         position error.
-        type: Different API for location fix. Use gnss/flp/nmea
+        api_type: Different API for location fix. Use gnss/flp/nmea
 
     Returns:
         ttff_data: A dict of all TTFF data.
@@ -1051,7 +1301,7 @@
             if ttff_sec != 0.0:
                 ttff_ant_cn = float(ttff_log[18].strip("]"))
                 ttff_base_cn = float(ttff_log[25].strip("]"))
-                if type == "gnss":
+                if api_type == "gnss":
                     gnss_results = ad.search_logcat("GPSService: Check item",
                                                     begin_time)
                     if gnss_results:
@@ -1067,7 +1317,7 @@
                         utc_time = epoch_to_human_time(loc_time)
                         ttff_haccu = float(
                             gnss_location_log[11].split("=")[-1].strip(","))
-                elif type == "flp":
+                elif api_type == "flp":
                     flp_results = ad.search_logcat("GPSService: FLP Location",
                                                    begin_time)
                     if flp_results:
@@ -1139,11 +1389,13 @@
                 % (len(ttff_data.keys()), ttff_mode))
     ad.log.info("%s PASS criteria is %d seconds" % (ttff_mode, criteria))
     ad.log.debug("%s TTFF data: %s" % (ttff_mode, ttff_data))
-    ttff_property_key_and_value(ad, ttff_data, ttff_mode)
     if len(ttff_data.keys()) == 0:
         ad.log.error("GTW_GPSTool didn't process TTFF properly.")
-        return False
-    elif any(float(ttff_data[key].ttff_sec) == 0.0 for key in ttff_data.keys()):
+        raise ValueError("No ttff loop is done")
+
+    ttff_property_key_and_value(ad, ttff_data, ttff_mode)
+
+    if any(float(ttff_data[key].ttff_sec) == 0.0 for key in ttff_data.keys()):
         ad.log.error("One or more TTFF %s Timeout" % ttff_mode)
         return False
     elif any(float(ttff_data[key].ttff_sec) >= criteria for key in
@@ -1165,6 +1417,7 @@
         ttff_data: TTFF data of secs, position error and signal strength.
         ttff_mode: TTFF Test mode for current test item.
     """
+    timeout_ttff = 61
     prop_basename = "TestResult "+ttff_mode.replace(" ", "_")+"_TTFF_"
     sec_list = [float(ttff_data[key].ttff_sec) for key in ttff_data.keys()]
     pe_list = [float(ttff_data[key].ttff_pe) for key in ttff_data.keys()]
@@ -1175,12 +1428,14 @@
     haccu_list = [float(ttff_data[key].ttff_haccu) for key in
                     ttff_data.keys()]
     timeoutcount = sec_list.count(0.0)
+    sec_list = sorted(sec_list)
     if len(sec_list) == timeoutcount:
-        avgttff = 9527
+        median_ttff = avgttff = timeout_ttff
     else:
         avgttff = sum(sec_list)/(len(sec_list) - timeoutcount)
+        median_ttff = median(sec_list)
     if timeoutcount != 0:
-        maxttff = 9527
+        maxttff = timeout_ttff
     else:
         maxttff = max(sec_list)
     avgdis = sum(pe_list)/len(pe_list)
@@ -1189,6 +1444,7 @@
     base_avgcn = sum(base_cn_list)/len(base_cn_list)
     avg_haccu = sum(haccu_list)/len(haccu_list)
     ad.log.info(prop_basename+"AvgTime %.1f" % avgttff)
+    ad.log.info(prop_basename+"MedianTime %.1f" % median_ttff)
     ad.log.info(prop_basename+"MaxTime %.1f" % maxttff)
     ad.log.info(prop_basename+"TimeoutCount %d" % timeoutcount)
     ad.log.info(prop_basename+"AvgDis %.1f" % avgdis)
@@ -1249,11 +1505,14 @@
 
     Args:
         ad: An AndroidDevice object.
+    Returns:
+        string: the current focused window / app
     """
     time.sleep(1)
     current = ad.adb.shell(
         "dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'")
     ad.log.debug("\n"+current)
+    return current
 
 
 def check_location_api(ad, retries):
@@ -1300,7 +1559,8 @@
     criteria = criteria * 1000
     search_pattern = ("GPSTool : networkLocationType = %s" % location_type)
     for i in range(retries):
-        begin_time = get_current_epoch_time()
+        # Capture the begin time 1 seconds before due to time gap.
+        begin_time = get_current_epoch_time() - 1000
         ad.log.info("Try to get NLP status - attempt %d" % (i+1))
         ad.adb.shell(
             "am start -S -n com.android.gpstool/.GPSTool --es mode nlp")
@@ -1404,6 +1664,11 @@
                             "but audio is not in MUSIC state")
 
 
+def get_gms_version(ad):
+    cmd = "dumpsys package com.google.android.gms | grep versionName"
+    return ad.adb.shell(cmd).split("\n")[0].split("=")[1]
+
+
 def get_baseband_and_gms_version(ad, extra_msg=""):
     """Get current radio baseband and GMSCore version of AndroidDevice object.
 
@@ -1417,9 +1682,7 @@
     try:
         build_version = ad.adb.getprop("ro.build.id")
         baseband_version = ad.adb.getprop("gsm.version.baseband")
-        gms_version = ad.adb.shell(
-            "dumpsys package com.google.android.gms | grep versionName"
-        ).split("\n")[0].split("=")[1]
+        gms_version = get_gms_version(ad)
         if check_chipset_vendor_by_qualcomm(ad):
             mpss_version = ad.adb.shell(
                 "cat /sys/devices/soc0/images | grep MPSS | cut -d ':' -f 3")
@@ -1947,7 +2210,7 @@
         raise signals.TestError("Failed to launch EEcoexer.")
 
 
-def excute_eecoexer_function(ad, eecoexer_args):
+def execute_eecoexer_function(ad, eecoexer_args):
     """Execute EEcoexer commands.
 
     Args:
@@ -1970,6 +2233,21 @@
     ad.adb.shell(wait_for_cmd)
 
 
+def get_process_pid(ad, process_name):
+    """Gets the process PID
+
+    Args:
+        ad: The device under test
+        process_name: The name of the process
+
+    Returns:
+        The PID of the process
+    """
+    command = f"ps -A | grep {process_name} |  awk '{{print $2}}'"
+    pid = ad.adb.shell(command)
+    return pid
+
+
 def restart_gps_daemons(ad):
     """Restart GPS daemons by killing services of gpsd, lhd and scd.
 
@@ -1979,22 +2257,22 @@
     gps_daemons_list = ["gpsd", "lhd", "scd"]
     ad.root_adb()
     for service in gps_daemons_list:
-        begin_time = get_current_epoch_time()
         time.sleep(3)
         ad.log.info("Kill GPS daemon \"%s\"" % service)
-        ad.adb.shell("killall %s" % service)
+        service_pid = get_process_pid(ad, service)
+        ad.log.debug("%s PID: %s" % (service, service_pid))
+        ad.adb.shell(f"kill -9 {service_pid}")
         # Wait 3 seconds for daemons and services to start.
         time.sleep(3)
-        restart_services = ad.search_logcat("starting service", begin_time)
-        for restart_service in restart_services:
-            if service in restart_service["log_message"]:
-                ad.log.info(restart_service["log_message"])
-                ad.log.info(
-                    "GPS daemon \"%s\" restarts successfully." % service)
-                break
-        else:
+
+        new_pid = get_process_pid(ad, service)
+        ad.log.debug("%s new PID: %s" % (service, new_pid))
+        if not new_pid or service_pid == new_pid:
             raise signals.TestError("Unable to restart \"%s\"" % service)
 
+        ad.log.info("GPS daemon \"%s\" restarts successfully. PID from %s to %s" % (
+            service, service_pid, new_pid))
+
 
 def is_device_wearable(ad):
     """Check device is wearable project or not.
@@ -2044,6 +2322,21 @@
         return None
 
 
+def _get_dpo_info_from_logcat(ad, begin_time):
+    """Gets the DPO info from logcat.
+
+    Args:
+        ad: The device under test.
+        begin_time: The start time of the log.
+    """
+    dpo_results = ad.search_logcat("HardwareClockDiscontinuityCount",
+                                   begin_time)
+    if not dpo_results:
+        raise signals.TestError(
+            "No \"HardwareClockDiscontinuityCount\" is found in logs.")
+    return dpo_results
+
+
 def check_dpo_rate_via_gnss_meas(ad, begin_time, dpo_threshold):
     """Check DPO engage rate through "HardwareClockDiscontinuityCount" in
     GnssMeasurement callback.
@@ -2054,11 +2347,7 @@
         dpo_threshold: The value to set threshold. (Ex: dpo_threshold = 60)
     """
     time_regex = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3})'
-    dpo_results = ad.search_logcat("HardwareClockDiscontinuityCount",
-                                   begin_time)
-    if not dpo_results:
-        raise signals.TestError(
-            "No \"HardwareClockDiscontinuityCount\" is found in logs.")
+    dpo_results = _get_dpo_info_from_logcat(ad, begin_time)
     ad.log.info(dpo_results[0]["log_message"])
     ad.log.info(dpo_results[-1]["log_message"])
     start_time = re.compile(
@@ -2083,13 +2372,14 @@
                                            threshold))
 
 
-def parse_brcm_nmea_log(ad, nmea_pattern, brcm_error_log_allowlist):
+def parse_brcm_nmea_log(ad, nmea_pattern, brcm_error_log_allowlist, stop_logger=True):
     """Parse specific NMEA pattern out of BRCM NMEA log.
 
     Args:
         ad: An AndroidDevice object.
         nmea_pattern: Specific NMEA pattern to parse.
         brcm_error_log_allowlist: Benign error logs to exclude.
+        stop_logger: To stop pixel logger or not.
 
     Returns:
         brcm_log_list: A list of specific NMEA pattern logs.
@@ -2097,58 +2387,68 @@
     brcm_log_list = []
     brcm_log_error_pattern = ["lhd: FS: Start Failsafe dump", "E slog"]
     brcm_error_log_list = []
-    stop_pixel_logger(ad)
     pixellogger_path = (
         "/sdcard/Android/data/com.android.pixellogger/files/logs/gps/.")
-    tmp_log_path = tempfile.mkdtemp()
-    ad.pull_files(pixellogger_path, tmp_log_path)
-    for path_key in os.listdir(tmp_log_path):
-        zip_path = posixpath.join(tmp_log_path, path_key)
-        if path_key.endswith(".zip"):
-            ad.log.info("Processing zip file: {}".format(zip_path))
-            with zipfile.ZipFile(zip_path, "r") as zip_file:
-                zip_file.extractall(tmp_log_path)
-                gl_logs = zip_file.namelist()
-                # b/214145973 check if hidden exists in pixel logger zip file
-                tmp_file = [name for name in gl_logs if 'tmp' in name]
-                if tmp_file:
-                    ad.log.warn(f"Hidden file {tmp_file} exists in pixel logger zip file")
-            break
-        elif os.path.isdir(zip_path):
-            ad.log.info("BRCM logs didn't zip properly. Log path is directory.")
-            tmp_log_path = zip_path
-            gl_logs = os.listdir(tmp_log_path)
-            ad.log.info("Processing BRCM log files: {}".format(gl_logs))
-            break
-    else:
-        raise signals.TestError(
-            "No BRCM logs found in {}".format(os.listdir(tmp_log_path)))
-    gl_logs = [log for log in gl_logs
-               if log.startswith("gl") and log.endswith(".log")]
-    for file in gl_logs:
-        nmea_log_path = posixpath.join(tmp_log_path, file)
-        ad.log.info("Parsing log pattern of \"%s\" in %s" % (nmea_pattern,
-                                                             nmea_log_path))
-        brcm_log = open(nmea_log_path, "r", encoding="UTF-8", errors="ignore")
-        lines = brcm_log.readlines()
-        for line in lines:
-            if nmea_pattern in line:
-                brcm_log_list.append(line)
-            for attr in brcm_log_error_pattern:
-                if attr in line:
-                    benign_log = False
-                    for allow_log in brcm_error_log_allowlist:
-                        if allow_log in line:
-                            benign_log = True
-                            ad.log.info("\"%s\" is in allow-list and removed "
-                                        "from error." % allow_log)
-                    if not benign_log:
-                        brcm_error_log_list.append(line)
+    if not isinstance(nmea_pattern, re.Pattern):
+        nmea_pattern = re.compile(nmea_pattern)
+
+    with tempfile.TemporaryDirectory() as tmp_dir:
+        try:
+            ad.pull_files(pixellogger_path, tmp_dir)
+        except AdbCommandError:
+            raise FileNotFoundError("No pixel logger folders found")
+
+        # Although we don't rely on the zip file, stop pixel logger here to avoid
+        # wasting resources.
+        if stop_logger:
+            stop_pixel_logger(ad)
+
+        tmp_path = pathlib.Path(tmp_dir)
+        log_folders = sorted([x for x in tmp_path.iterdir() if x.is_dir()])
+        if not log_folders:
+            raise FileNotFoundError("No BRCM logs found.")
+        # The folder name is a string of datetime, the latest one will be in the last index.
+        gl_logs = log_folders[-1].glob("**/gl*.log")
+
+        for nmea_log_path in gl_logs:
+            ad.log.info("Parsing log pattern of \"%s\" in %s" % (nmea_pattern,
+                                                                 nmea_log_path))
+            with open(nmea_log_path, "r", encoding="UTF-8", errors="ignore") as lines:
+                for line in lines:
+                    line = line.strip()
+                    if nmea_pattern.fullmatch(line):
+                        brcm_log_list.append(line)
+                    for attr in brcm_log_error_pattern:
+                        if attr in line:
+                            benign_log = False
+                            for regex_pattern in brcm_error_log_allowlist:
+                                if re.search(regex_pattern, line):
+                                    benign_log = True
+                                    ad.log.debug("\"%s\" is in allow-list and removed "
+                                                "from error." % line)
+                            if not benign_log:
+                                brcm_error_log_list.append(line)
+
     brcm_error_log = "".join(brcm_error_log_list)
-    shutil.rmtree(tmp_log_path, ignore_errors=True)
     return brcm_log_list, brcm_error_log
 
 
+def _get_power_mode_log_from_pixel_logger(ad, brcm_error_log_allowlist, stop_pixel_logger=True):
+    """Gets the power log from pixel logger.
+
+    Args:
+        ad: The device under test.
+        brcm_error_log_allow_list: The allow list to ignore certain error in pixel logger.
+        stop_pixel_logger: To disable pixel logger when getting the log.
+    """
+    pglor_list, brcm_error_log = parse_brcm_nmea_log(
+        ad, _BRCM_DUTY_CYCLE_PATTERN, brcm_error_log_allowlist, stop_pixel_logger)
+    if not pglor_list:
+        raise signals.TestFailure("Fail to get DPO logs from pixel logger")
+
+    return pglor_list, brcm_error_log
+
+
 def check_dpo_rate_via_brcm_log(ad, dpo_threshold, brcm_error_log_allowlist):
     """Check DPO engage rate through "$PGLOR,11,STA" in BRCM Log.
     D - Disabled, Always full power.
@@ -2164,10 +2464,7 @@
     always_full_power_count = 0
     full_power_count = 0
     power_save_count = 0
-    pglor_list, brcm_error_log = parse_brcm_nmea_log(
-        ad, "$PGLOR,11,STA", brcm_error_log_allowlist)
-    if not pglor_list:
-        raise signals.TestFailure("Fail to get DPO logs from pixel logger")
+    pglor_list, brcm_error_log = _get_power_mode_log_from_pixel_logger(ad, brcm_error_log_allowlist)
 
     for pglor in pglor_list:
         power_res = re.compile(r',P,(\w),').search(pglor).group(1)
@@ -2199,74 +2496,77 @@
                                   brcm_error_log))
 
 
-def pair_to_wearable(ad, ad1):
-    """Pair phone to watch via Bluetooth.
+def process_pair(watch, phone):
+    """Pair phone to watch via Bluetooth in OOBE.
 
     Args:
-        ad: A pixel phone.
-        ad1: A wearable project.
+        watch: A wearable project.
+        phone: A pixel phone.
     """
-    check_location_service(ad1)
-    utils.sync_device_time(ad1)
-    bt_model_name = ad.adb.getprop("ro.product.model")
-    bt_sn_name = ad.adb.getprop("ro.serialno")
+    check_location_service(phone)
+    utils.sync_device_time(phone)
+    bt_model_name = watch.adb.getprop("ro.product.model")
+    bt_sn_name = watch.adb.getprop("ro.serialno")
     bluetooth_name = bt_model_name +" " + bt_sn_name[10:]
-    fastboot_factory_reset(ad, False)
-    ad.log.info("Wait 1 min for wearable system busy time.")
+    fastboot_factory_reset(watch, False)
+    # TODO (chenstanley)Need to re-structure for better code and test flow instead of simply waiting
+    watch.log.info("Wait 1 min for wearable system busy time.")
     time.sleep(60)
-    ad.adb.shell("input keyevent 4")
+    watch.adb.shell("input keyevent 4")
     # Clear Denali paired data in phone.
-    ad1.adb.shell("pm clear com.google.android.gms")
-    ad1.adb.shell("pm clear com.google.android.apps.wear.companion")
-    ad1.adb.shell("am start -S -n com.google.android.apps.wear.companion/"
+    phone.adb.shell("pm clear com.google.android.gms")
+    phone.adb.shell("pm clear com.google.android.apps.wear.companion")
+    phone.adb.shell("am start -S -n com.google.android.apps.wear.companion/"
                         "com.google.android.apps.wear.companion.application.RootActivity")
-    uia_click(ad1, "Next")
-    uia_click(ad1, "I agree")
-    uia_click(ad1, bluetooth_name)
-    uia_click(ad1, "Pair")
-    uia_click(ad1, "Skip")
-    uia_click(ad1, "Skip")
-    uia_click(ad1, "Finish")
-    ad.log.info("Wait 3 mins for complete pairing process.")
+    uia_click(phone, "Continue")
+    uia_click(phone, "More")
+    uia_click(phone, "I agree")
+    uia_click(phone, "I accept")
+    uia_click(phone, bluetooth_name)
+    uia_click(phone, "Pair")
+    uia_click(phone, "Skip")
+    uia_click(phone, "Next")
+    uia_click(phone, "Skip")
+    uia_click(phone, "Done")
+    # TODO (chenstanley)Need to re-structure for better code and test flow instead of simply waiting
+    watch.log.info("Wait 3 mins for complete pairing process.")
     time.sleep(180)
-    ad.adb.shell("settings put global stay_on_while_plugged_in 7")
-    check_location_service(ad)
-    enable_gnss_verbose_logging(ad)
-    if is_bluetooth_connected(ad, ad1):
-        ad.log.info("Pairing successfully.")
-    else:
-        raise signals.TestFailure("Fail to pair watch and phone successfully.")
+    set_screen_always_on(watch)
+    check_location_service(watch)
+    enable_gnss_verbose_logging(watch)
 
 
-def is_bluetooth_connected(ad, ad1):
+def is_bluetooth_connected(watch, phone):
     """Check if device's Bluetooth status is connected or not.
 
     Args:
-    ad: A wearable project
-    ad1: A pixel phone.
+    watch: A wearable project
+    phone: A pixel phone.
     """
-    return ad.droid.bluetoothIsDeviceConnected(ad1.droid.bluetoothGetLocalAddress())
+    return watch.droid.bluetoothIsDeviceConnected(phone.droid.bluetoothGetLocalAddress())
 
 
-def detect_crash_during_tracking(ad, begin_time, type):
+def detect_crash_during_tracking(ad, begin_time, api_type, ignore_hal_crash=False):
     """Check if GNSS or GPSTool crash happened druing GNSS Tracking.
 
     Args:
     ad: An AndroidDevice object.
     begin_time: Start Time to check if crash happened in logs.
-    type: Using GNSS or FLP reading method in GNSS tracking.
+    api_type: Using GNSS or FLP reading method in GNSS tracking.
+    ignore_hal_crash: In BRCM devices, once the HAL is being killed, it will write error/fatal logs.
+      Ignore this error if the error logs are expected.
     """
     gnss_crash_list = [".*Fatal signal.*gnss",
-                       ".*Fatal signal.*xtra",
-                       ".*F DEBUG.*gnss",
-                       ".*Fatal signal.*gpsd"]
+                       ".*Fatal signal.*xtra"]
+    if not ignore_hal_crash:
+        gnss_crash_list += [".*Fatal signal.*gpsd", ".*F DEBUG.*gnss"]
     if not ad.is_adb_logcat_on:
         ad.start_adb_logcat()
     for attr in gnss_crash_list:
         gnss_crash_result = ad.adb.shell(
-            "logcat -d | grep -E -i '%s'" % attr)
+            "logcat -d | grep -E -i '%s'" % attr, ignore_status=True, timeout = 300)
         if gnss_crash_result:
-            start_gnss_by_gtw_gpstool(ad, state=False, type=type)
+            start_gnss_by_gtw_gpstool(ad, state=False, api_type=api_type)
             raise signals.TestFailure(
                 "Test failed due to GNSS HAL crashed. \n%s" %
                 gnss_crash_result)
@@ -2381,57 +2681,103 @@
     ad.log.info("Delete BCM's NVMEM ephemeris files.\n%s" % status)
 
 
-def bcm_gps_xml_add_option(ad,
+def bcm_gps_xml_update_option(ad,
+                           option,
                            search_line=None,
                            append_txt=None,
+                           update_txt=None,
+                           delete_txt=None,
                            gps_xml_path=BCM_GPS_XML_PATH):
     """Append parameter setting in gps.xml for BCM solution
 
     Args:
+        option: A str to identify the operation (add/update/delete).
         ad: An AndroidDevice object.
         search_line: Pattern matching of target
         line for appending new line data.
-        append_txt: New line that will be appended after the search_line.
+        append_txt: New lines that will be appended after the search_line.
+        update_txt: New line to update the original file.
+        delete_txt: lines to delete from the original file.
         gps_xml_path: gps.xml file location of DUT
     """
     remount_device(ad)
     #Update gps.xml
-    if not search_line or not append_txt:
-        ad.log.info("Nothing for update.")
-    else:
-        tmp_log_path = tempfile.mkdtemp()
-        ad.pull_files(gps_xml_path, tmp_log_path)
-        gps_xml_tmp_path = os.path.join(tmp_log_path, "gps.xml")
-        gps_xml_file = open(gps_xml_tmp_path, "r")
-        lines = gps_xml_file.readlines()
-        gps_xml_file.close()
-        fout = open(gps_xml_tmp_path, "w")
-        append_txt_tag = append_txt.strip()
+    tmp_log_path = tempfile.mkdtemp()
+    ad.pull_files(gps_xml_path, tmp_log_path)
+    gps_xml_tmp_path = os.path.join(tmp_log_path, "gps.xml")
+    gps_xml_file = open(gps_xml_tmp_path, "r")
+    lines = gps_xml_file.readlines()
+    gps_xml_file.close()
+    fout = open(gps_xml_tmp_path, "w")
+    if option == "add":
         for line in lines:
-            if append_txt_tag in line:
-                ad.log.info('{} is already in the file. Skip'.format(append_txt))
+            if line.strip() in append_txt:
+                ad.log.info("{} is already in the file. Skip".format(append_txt))
                 continue
             fout.write(line)
             if search_line in line:
-                fout.write(append_txt)
-                ad.log.info("Update new line: '{}' in gps.xml.".format(append_txt))
-        fout.close()
+                for add_txt in append_txt:
+                    fout.write(add_txt)
+                    ad.log.info("Add new line: '{}' in gps.xml.".format(add_txt))
+    elif option == "update":
+        for line in lines:
+            if search_line in line:
+                ad.log.info(line)
+                fout.write(update_txt)
+                ad.log.info("Update line: '{}' in gps.xml.".format(update_txt))
+                continue
+            fout.write(line)
+    elif option == "delete":
+        for line in lines:
+            if delete_txt in line:
+                ad.log.info("Delete line: '{}' in gps.xml.".format(line.strip()))
+                continue
+            fout.write(line)
+    fout.close()
 
-        # Update gps.xml with gps_new.xml
-        ad.push_system_file(gps_xml_tmp_path, gps_xml_path)
+    # Update gps.xml with gps_new.xml
+    ad.push_system_file(gps_xml_tmp_path, gps_xml_path)
 
-        # remove temp folder
-        shutil.rmtree(tmp_log_path, ignore_errors=True)
+    # remove temp folder
+    shutil.rmtree(tmp_log_path, ignore_errors=True)
 
+def bcm_gps_ignore_warmstandby(ad):
+    """ remove warmstandby setting in BCM gps.xml to reset tracking filter
+    Args:
+        ad: An AndroidDevice object.
+    """
+    search_line_tag = '<gll\n'
+    delete_line_str = 'WarmStandbyTimeout1Seconds'
+    bcm_gps_xml_update_option(ad,
+                              "delete",
+                              search_line_tag,
+                              append_txt=None,
+                              update_txt=None,
+                              delete_txt=delete_line_str)
+
+    search_line_tag = '<gll\n'
+    delete_line_str = 'WarmStandbyTimeout2Seconds'
+    bcm_gps_xml_update_option(ad,
+                              "delete",
+                              search_line_tag,
+                              append_txt=None,
+                              update_txt=None,
+                              delete_txt=delete_line_str)
 
 def bcm_gps_ignore_rom_alm(ad):
     """ Update BCM gps.xml with ignoreRomAlm="True"
     Args:
         ad: An AndroidDevice object.
     """
+    search_line_tag = '<hal\n'
+    append_line_str = ['       IgnoreJniTime=\"true\"\n']
+    bcm_gps_xml_update_option(ad, "add", search_line_tag, append_line_str)
+
     search_line_tag = '<gll\n'
-    append_line_str = '       IgnoreRomAlm=\"true\"\n'
-    bcm_gps_xml_add_option(ad, search_line_tag, append_line_str)
+    append_line_str = ['       IgnoreRomAlm=\"true\"\n',
+                       '       AutoColdStartSignal=\"SIMULATED\"\n',
+                       '       IgnoreJniTime=\"true\"\n']
+    bcm_gps_xml_update_option(ad, "add", search_line_tag, append_line_str)
 
 
 def check_inject_time(ad):
@@ -2449,35 +2795,477 @@
             return True
     raise signals.TestFailure("Fail to get time injected within %s attempts." % i)
 
+def recover_paired_status(watch, phone):
+    """Recover Bluetooth paired status if not paired.
 
-def enable_framework_log(ad):
-    """Enable framework log for wearable to check UTC time download.
+    Args:
+        watch: A wearable project.
+        phone: A pixel phone.
+    """
+    for _ in range(3):
+        watch.log.info("Switch Bluetooth Off-On to recover paired status.")
+        for status in (False, True):
+            watch.droid.bluetoothToggleState(status)
+            phone.droid.bluetoothToggleState(status)
+            # TODO (chenstanley)Need to re-structure for better code and test flow instead of simply waiting
+            watch.log.info("Wait for Bluetooth auto re-connect.")
+            time.sleep(10)
+        if is_bluetooth_connected(watch, phone):
+            watch.log.info("Success to recover paired status.")
+            return True
+    raise signals.TestFailure("Fail to recover BT paired status in 3 attempts.")
+
+def push_lhd_overlay(ad):
+    """Push lhd_overlay.conf to device in /data/vendor/gps/overlay/
+
+    ad:
+        ad: An AndroidDevice object.
+    """
+    overlay_name = "lhd_overlay.conf"
+    overlay_asset = ad.adb.shell("ls /data/vendor/gps/overlay/")
+    if overlay_name in overlay_asset:
+        ad.log.info(f"{overlay_name} already in device, skip.")
+        return
+
+    temp_path = tempfile.mkdtemp()
+    file_path = os.path.join(temp_path, overlay_name)
+    lhd_content = 'Lhe477xDebugFlags=RPC:FACILITY=2097151:LOG_INFO:STDOUT_PUTS:STDOUT_LOG\n'\
+                  'LogLevel=*:E\nLogLevel=*:W\nLogLevel=*:I\nLog=LOGCAT\nLogEnabled=true\n'
+    overlay_path = "/data/vendor/gps/overlay/"
+    with open(file_path, "w") as f:
+        f.write(lhd_content)
+    ad.log.info("Push lhd_overlay to device")
+    ad.adb.push(file_path, overlay_path)
+
+
+def disable_ramdump(ad):
+    """Disable ramdump so device will reboot when about to enter ramdump
+
+    Once device enter ramdump, it will take a while to generate dump file
+    The process may take a while and block all the tests.
+    By disabling the ramdump mode, device will reboot instead of entering ramdump mode
 
     Args:
         ad: An AndroidDevice object.
     """
-    remount_device(ad)
-    time.sleep(3)
-    ad.log.info("Start to enable framwork log for wearable.")
-    ad.adb.shell("echo 'log.tag.LocationManagerService=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.GnssLocationProvider=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.GpsNetInitiatedHandler=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.GnssNetInitiatedHandler=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.GnssNetworkConnectivityHandler=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.NtpTimeHelper=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.ConnectivityService=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.GnssPsdsDownloader=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.GnssVisibilityControl=VERBOSE'  >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.Gnss=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.GnssConfiguration=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.ImsPhone=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.GsmCdmaPhone=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.Phone=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("echo 'log.tag.GCoreFlp=VERBOSE' >> /data/local.prop")
-    ad.adb.shell("chmod 644 /data/local.prop")
-    ad.adb.shell("echo 'LogEnabled=true' > /data/vendor/gps/libgps.conf")
-    ad.adb.shell("chown gps.system /data/vendor/gps/libgps.conf")
-    ad.adb.shell("sync")
-    reboot(ad)
-    ad.log.info("Wait 2 mins for Wearable booting system busy")
-    time.sleep(120)
+    ad.log.info("Enter bootloader mode")
+    ad.stop_services()
+    ad.adb.reboot("bootloader")
+    for _ in range(1,9):
+        if ad.is_bootloader:
+            break
+        time.sleep(1)
+    else:
+        raise signals.TestFailure("can't enter bootloader mode")
+    ad.log.info("Disable ramdump")
+    ad.fastboot.oem("ramdump disable")
+    ad.fastboot.reboot()
+    ad.wait_for_boot_completion()
+    ad.root_adb()
+    tutils.bring_up_sl4a(ad)
+    ad.start_adb_logcat()
+
+
+def deep_suspend_device(ad):
+    """Force DUT to enter deep suspend mode
+
+    When DUT is connected to PCs, it won't enter deep suspend mode
+    by pressing power button.
+
+    To force DUT enter deep suspend mode, we need to send the
+    following command to DUT  "echo mem >/sys/power/state"
+
+    To make sure the DUT stays in deep suspend mode for a while,
+    it will send the suspend command 3 times with 15s interval
+
+    Args:
+        ad: An AndroidDevice object.
+    """
+    ad.log.info("Ready to go to deep suspend mode")
+    begin_time = get_device_time(ad)
+    ad.droid.goToSleepNow()
+    ensure_power_manager_is_dozing(ad, begin_time)
+    ad.stop_services()
+    try:
+        command = "echo deep > /sys/power/mem_sleep && echo mem > /sys/power/state"
+        for i in range(1, 4):
+            ad.log.debug(f"Send deep suspend command round {i}")
+            ad.adb.shell(command, ignore_status=True)
+            # sleep here to ensure the device stays enough time in deep suspend mode
+            time.sleep(15)
+            if not _is_device_enter_deep_suspend(ad):
+                raise signals.TestFailure("Device didn't enter deep suspend mode")
+        ad.log.info("Wake device up now")
+    except Exception:
+        # when exception happen, it's very likely the device is rebooting
+        # to ensure the test can go on, wait for the device is ready
+        ad.log.warn("Device may be rebooting, wait for it")
+        ad.wait_for_boot_completion()
+        ad.root_adb()
+        raise
+    finally:
+        tutils.bring_up_sl4a(ad)
+        ad.start_adb_logcat()
+        ad.droid.wakeUpNow()
+
+
+def get_device_time(ad):
+    """Get current datetime from device
+
+    Args:
+        ad: An AndroidDevice object.
+
+    Returns:
+        datetime object
+    """
+    result = ad.adb.shell("date +\"%Y-%m-%d %T.%3N\"")
+    return datetime.strptime(result, "%Y-%m-%d %H:%M:%S.%f")
+
+
+def ensure_power_manager_is_dozing(ad, begin_time):
+    """Check if power manager is in dozing
+    When device is sleeping, power manager should goes to doze mode.
+    To ensure that, we check the log every 1 second (maximum to 3 times)
+
+    Args:
+        ad: An AndroidDevice object.
+        begin_time: datetime, used as the starting point to search log
+    """
+    keyword = "PowerManagerService: Dozing"
+    ad.log.debug("Log search start time: %s" % begin_time)
+    for i in range(0,3):
+        result = ad.search_logcat(keyword, begin_time)
+        if result:
+            break
+        ad.log.debug("Power manager is not dozing... retry in 1 second")
+        time.sleep(1)
+    else:
+        ad.log.warn("Power manager didn't enter dozing")
+
+
+
+def _is_device_enter_deep_suspend(ad):
+    """Check device has been enter deep suspend mode
+
+    If device has entered deep suspend mode, we should be able to find keyword
+    "suspend entry (deep)"
+
+    Args:
+        ad: An AndroidDevice object.
+
+    Returns:
+        bool: True / False -> has / has not entered deep suspend
+    """
+    cmd = f"adb -s {ad.serial} logcat -d|grep \"suspend entry (deep)\""
+    process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                               stderr=subprocess.PIPE, shell=True)
+    result, _ = process.communicate()
+    ad.log.debug(f"suspend result = {result}")
+
+    return bool(result)
+
+
+def check_location_report_interval(ad, location_reported_time_src, total_seconds, tolerance):
+    """Validate the interval between two location reported time
+    Normally the interval should be around 1 second but occasionally it may up to nearly 2 seconds
+    So we set up a tolerance - 99% of reported interval should be less than 1.3 seconds
+
+    We validate the interval backward, because the wrong interval mostly happened at the end
+    Args:
+        ad: An AndroidDevice object.
+        location_reported_time_src: A list of reported time(in string) from GPS tool
+        total_seconds: (int) how many seconds has the GPS been enabled
+        tolerance: (float) set how many ratio of error should be accepted
+                   if we want to set tolerance to be 1% then pass 0.01 as tolerance value
+    """
+    ad.log.info("Checking location report frequency")
+    error_count = 0
+    error_tolerance = max(1, int(total_seconds * tolerance))
+    expected_longest_interval = 1.3
+    location_reported_time = list(map(lambda x: datetime.strptime(x, "%Y/%m/%d %H:%M:%S.%f"),
+                                      location_reported_time_src))
+    location_reported_time = sorted(location_reported_time)
+    last_gps_report_time = location_reported_time[-1]
+    ad.log.debug("Location report time: %s" % location_reported_time)
+
+    for reported_time in reversed(location_reported_time):
+        time_diff = last_gps_report_time - reported_time
+        if time_diff.total_seconds() > expected_longest_interval:
+            error_count += 1
+        last_gps_report_time = reported_time
+
+    if error_count > error_tolerance:
+        fail_message = (f"Interval longer than {expected_longest_interval}s "
+                        f"exceed tolerance count: {error_tolerance}, error count: {error_count}")
+        ad.log.error(fail_message)
+
+
+@contextmanager
+def set_screen_status(ad, off=True):
+    """Set screen on / off
+
+    A context manager function, can be used with "with" statement.
+    example:
+        with set_screen_status(ad, off=True):
+            do anything you want during screen is off
+    Once the function end, it will turn on the screen
+    Args:
+        ad: AndroidDevice object
+        off: (bool) True -> turn off screen / False -> leave screen as it is
+    """
+    try:
+        if off:
+            ad.droid.goToSleepNow()
+        yield ad
+    finally:
+        ad.droid.wakeUpNow()
+        ensure_device_screen_is_on(ad)
+
+
+@contextmanager
+def full_gnss_measurement(ad):
+    """Context manager function to enable full gnss measurement"""
+    try:
+        ad.adb.shell("settings put global enable_gnss_raw_meas_full_tracking 1")
+        yield ad
+    finally:
+        ad.adb.shell("settings put global enable_gnss_raw_meas_full_tracking 0")
+
+
+def ensure_device_screen_is_on(ad):
+    """Make sure the screen is on
+
+    Will try 3 times, each with 1 second interval
+
+    Raise:
+        GnssTestUtilsError: if screen can't be turn on after 3 tries
+    """
+    for _ in range(3):
+        # when NotificationShade appears in focus window, it indicates the screen is still off
+        if "NotificationShade" not in check_current_focus_app(ad):
+            break
+        time.sleep(1)
+    else:
+        raise GnssTestUtilsError("Device screen is not on after 3 tries")
+
+
+def start_qxdm_and_tcpdump_log(ad, enable):
+    """Start QXDM and adb tcpdump if collect_logs is True.
+    Args:
+        ad: AndroidDevice object
+        enable: (bool) True -> start collecting
+                       False -> not start collecting
+    """
+    if enable:
+        start_pixel_logger(ad)
+        tlutils.start_adb_tcpdump(ad)
+
+
+def set_screen_always_on(ad):
+    """Ensure the sceen will not turn off and display the correct app screen
+    for wearable, we also disable the charing screen,
+    otherwise the charing screen will keep popping up and block the GPS tool
+    """
+    if is_device_wearable(ad):
+        ad.adb.shell("settings put global stay_on_while_plugged_in 7")
+        ad.adb.shell("setprop persist.enable_charging_experience false")
+    else:
+        ad.adb.shell("settings put system screen_off_timeout 1800000")
+
+
+def validate_adr_rate(ad, pass_criteria):
+    """Check the ADR rate
+
+    Args:
+        ad: AndroidDevice object
+        pass_criteria: (float) the passing ratio, 1 = 100%, 0.5 = 50%
+    """
+    adr_statistic = GnssMeasurement(ad).get_adr_static()
+
+    ad.log.info("ADR threshold: {0:.1%}".format(pass_criteria))
+    ad.log.info(UPLOAD_TO_SPONGE_PREFIX + "ADR_valid_rate {0:.1%}".format(adr_statistic.valid_rate))
+    ad.log.info(UPLOAD_TO_SPONGE_PREFIX +
+                "ADR_usable_rate {0:.1%}".format(adr_statistic.usable_rate))
+    ad.log.info(UPLOAD_TO_SPONGE_PREFIX + "ADR_total_count %s" % adr_statistic.total_count)
+    ad.log.info(UPLOAD_TO_SPONGE_PREFIX + "ADR_valid_count %s" % adr_statistic.valid_count)
+    ad.log.info(UPLOAD_TO_SPONGE_PREFIX + "ADR_reset_count %s" % adr_statistic.reset_count)
+    ad.log.info(UPLOAD_TO_SPONGE_PREFIX +
+                "ADR_cycle_slip_count %s" % adr_statistic.cycle_slip_count)
+    ad.log.info(UPLOAD_TO_SPONGE_PREFIX +
+                "ADR_half_cycle_reported_count %s" % adr_statistic.half_cycle_reported_count)
+    ad.log.info(UPLOAD_TO_SPONGE_PREFIX +
+                "ADR_half_cycle_resolved_count %s" % adr_statistic.half_cycle_resolved_count)
+
+    asserts.assert_true(
+        (pass_criteria < adr_statistic.valid_rate) and (pass_criteria < adr_statistic.usable_rate),
+        f"ADR valid rate: {adr_statistic.valid_rate:.1%}, "
+        f"ADR usable rate: {adr_statistic.usable_rate:.1%} "
+        f"Lower than expected: {pass_criteria:.1%}"
+    )
+
+
+def pair_to_wearable(watch, phone):
+    """Pair watch to phone.
+
+    Args:
+        watch: A wearable project.
+        phone: A pixel phone.
+    Raise:
+        TestFailure: If pairing process could not success after 3 tries.
+    """
+    for _ in range(3):
+        process_pair(watch, phone)
+        if is_bluetooth_connected(watch, phone):
+            watch.log.info("Pairing successfully.")
+            return True
+    raise signals.TestFailure("Pairing is not successfully.")
+
+
+def disable_battery_defend(ad):
+    """Disable battery defend config to prevent battery defend message pop up
+    after connecting to the same charger for 4 days in a row.
+
+    Args:
+        ad: A wearable project.
+    """
+    for _ in range(5):
+        remount_device(ad)
+        ad.adb.shell("setprop vendor.battery.defender.disable 1")
+        # To simulate cable unplug and the status will be recover after device reboot.
+        ad.adb.shell("cmd battery unplug")
+        # Sleep 3 seconds for waiting adb commend changes config and simulates cable unplug.
+        time.sleep(3)
+        config_setting = ad.adb.shell("getprop vendor.battery.defender.state")
+        if config_setting == "DISABLED":
+            ad.log.info("Disable Battery Defend setting successfully.")
+            break
+
+
+def restart_hal_service(ad):
+    """Restart HAL service by killing the pid.
+
+    Gets the pid by ps command and pass the pid to kill command. Then we get the pid of HAL service
+    again to see if the pid changes(pid should be different after HAL restart). If not, we will
+    retry up to 4 times before raising Test Failure.
+
+    Args:
+        ad: AndroidDevice object
+    """
+    ad.log.info("Restart HAL service")
+    hal_process_name = "'android.hardware.gnss@[[:digit:]]\{1,2\}\.[[:digit:]]\{1,2\}-service'"
+    hal_pid = get_process_pid(ad, hal_process_name)
+    ad.log.info("HAL pid: %s" % hal_pid)
+
+    # Retry kill process if the PID is the same as original one
+    for _ in range(4):
+        ad.log.info("Kill HAL service")
+        ad.adb.shell(f"kill -9 {hal_pid}")
+
+        # Waits for the HAL service to restart up to 4 seconds.
+        for _ in range(4):
+            new_hal_pid = get_process_pid(ad, hal_process_name)
+            ad.log.info("New HAL pid: %s" % new_hal_pid)
+            if new_hal_pid:
+                if hal_pid != new_hal_pid:
+                    return
+                break
+            time.sleep(1)
+    else:
+        raise signals.TestFailure("HAL service can't be killed")
+
+
+def run_ttff(ad, mode, criteria, test_cycle, base_lat_long, collect_logs=False):
+    """Verify TTFF functionality with mobile data.
+
+    Args:
+        mode: "cs", "ws" or "hs"
+        criteria: Criteria for the test.
+
+    Returns:
+        ttff_data: A dict of all TTFF data.
+    """
+    start_qxdm_and_tcpdump_log(ad, collect_logs)
+    return run_ttff_via_gtw_gpstool(ad, mode, criteria, test_cycle, base_lat_long)
+
+
+def re_register_measurement_callback(dut):
+    """Send command to unregister then register measurement callback.
+
+    Args:
+        dut: The device under test.
+    """
+    dut.log.info("Reregister measurement callback")
+    dut.adb.shell("am broadcast -a com.android.gpstool.stop_meas_action")
+    time.sleep(1)
+    dut.adb.shell("am broadcast -a com.android.gpstool.start_meas_action")
+    time.sleep(1)
+
+
+def check_power_save_mode_status(ad, full_power, begin_time, brcm_error_allowlist):
+    """Checks the power save mode status.
+
+    For Broadcom:
+        Gets NEMA sentences from pixel logger and retrieve the status [F, S, D].
+        F,S => not in full power mode
+        D => in full power mode
+    For Qualcomm:
+        Gets the HardwareClockDiscontinuityCount from logcat. In full power mode, the
+        HardwareClockDiscontinuityCount should not be increased.
+
+    Args:
+        ad: The device under test.
+        full_power: The device is in full power mode or not.
+        begin_time: It is used to get the correct logcat information for qualcomm.
+        brcm_error_allowlist: It is used to ignore certain error in pixel logger.
+    """
+    if check_chipset_vendor_by_qualcomm(ad):
+        _check_qualcomm_power_save_mode(ad, full_power, begin_time)
+    else:
+        _check_broadcom_power_save_mode(ad, full_power, brcm_error_allowlist)
+
+
+def _check_qualcomm_power_save_mode(ad, full_power, begin_time):
+    dpo_results = _get_dpo_info_from_logcat(ad, begin_time)
+    first_dpo_count = int(dpo_results[0]["log_message"].split()[-1])
+    final_dpo_count = int(dpo_results[-1]["log_message"].split()[-1])
+    dpo_count_diff = final_dpo_count - first_dpo_count
+    ad.log.debug("The DPO count diff is {diff}".format(diff=dpo_count_diff))
+    if full_power:
+        asserts.assert_equal(dpo_count_diff, 0, msg="DPO count diff should be 0")
+    else:
+        asserts.assert_true(dpo_count_diff > 0, msg="DPO count diff should be more than 0")
+
+
+def _check_broadcom_power_save_mode(ad, full_power, brcm_error_allowlist):
+    power_save_log, _ = _get_power_mode_log_from_pixel_logger(
+        ad, brcm_error_allowlist, stop_pixel_logger=False)
+    power_status = re.compile(r',P,(\w),').search(power_save_log[-2]).group(1)
+    ad.log.debug("The power status is {status}".format(status=power_status))
+    if full_power:
+        asserts.assert_true(power_status == "D", msg="Should be in full power mode")
+    else:
+        asserts.assert_true(power_status in ["F", "S"], msg="Should not be in full power mode")
+
+@contextmanager
+def run_gnss_tracking(ad, criteria, meas_flag):
+    """A context manager to enable gnss tracking and stops at the end.
+
+    Args:
+        ad: The device under test.
+        criteria: The criteria for First Fixed.
+        meas_flag: A flag to turn on measurement log or not.
+    """
+    process_gnss_by_gtw_gpstool(ad, criteria=criteria, meas_flag=meas_flag)
+    try:
+        yield
+    finally:
+        start_gnss_by_gtw_gpstool(ad, state=False)
+
+def log_current_epoch_time(ad, sponge_key):
+    """Logs current epoch timestamp in second.
+
+    Args:
+        sponge_key: The key name of the sponge property.
+    """
+    current_epoch_time = get_current_epoch_time() // 1000
+    ad.log.info(f"TestResult {sponge_key} {current_epoch_time}")
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/gnss/gnss_testlog_utils.py b/acts_tests/acts_contrib/test_utils/gnss/gnss_testlog_utils.py
index 4f5df3c..5e7cf26 100644
--- a/acts_tests/acts_contrib/test_utils/gnss/gnss_testlog_utils.py
+++ b/acts_tests/acts_contrib/test_utils/gnss/gnss_testlog_utils.py
@@ -209,17 +209,17 @@
         configs=CONFIG_GPSTTFFLOG,
     )
     ttff_df = parsed_data['ttff_info']
-
-    # Data Conversion
-    ttff_df['loop'] = ttff_df['loop'].astype(int)
-    ttff_df['start_datetime'] = pds.to_datetime(ttff_df['start_datetime'])
-    ttff_df['stop_datetime'] = pds.to_datetime(ttff_df['stop_datetime'])
-    ttff_df['ttff_time'] = ttff_df['ttff'].astype(float)
-    ttff_df['ant_avg_top4_cn0'] = ttff_df['ant_avg_top4_cn0'].astype(float)
-    ttff_df['ant_avg_cn0'] = ttff_df['ant_avg_cn0'].astype(float)
-    ttff_df['bb_avg_top4_cn0'] = ttff_df['bb_avg_top4_cn0'].astype(float)
-    ttff_df['bb_avg_cn0'] = ttff_df['bb_avg_cn0'].astype(float)
-    ttff_df['satnum_for_fix'] = ttff_df['satnum_for_fix'].astype(int)
+    if not ttff_df.empty:
+        # Data Conversion
+        ttff_df['loop'] = ttff_df['loop'].astype(int)
+        ttff_df['start_datetime'] = pds.to_datetime(ttff_df['start_datetime'])
+        ttff_df['stop_datetime'] = pds.to_datetime(ttff_df['stop_datetime'])
+        ttff_df['ttff_time'] = ttff_df['ttff'].astype(float)
+        ttff_df['ant_avg_top4_cn0'] = ttff_df['ant_avg_top4_cn0'].astype(float)
+        ttff_df['ant_avg_cn0'] = ttff_df['ant_avg_cn0'].astype(float)
+        ttff_df['bb_avg_top4_cn0'] = ttff_df['bb_avg_top4_cn0'].astype(float)
+        ttff_df['bb_avg_cn0'] = ttff_df['bb_avg_cn0'].astype(float)
+        ttff_df['satnum_for_fix'] = ttff_df['satnum_for_fix'].astype(int)
 
     # return ttff dataframe
     return ttff_df
diff --git a/acts_tests/acts_contrib/test_utils/gnss/gnssstatus_utils.py b/acts_tests/acts_contrib/test_utils/gnss/gnssstatus_utils.py
new file mode 100644
index 0000000..457ba86
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/gnss/gnssstatus_utils.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2020 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 re
+from acts import signals
+from collections import defaultdict
+
+SVID_RANGE = {
+    'GPS': [(1, 32)],
+    'SBA': [(120, 192)],
+    'GLO': [(1, 24), (93, 106)],
+    'QZS': [(193, 200)],
+    'BDS': [(1, 63)],
+    'GAL': [(1, 36)],
+    'NIC': [(1, 14)]
+}
+
+CARRIER_FREQUENCIES = {
+    'GPS': {
+        'L1': [1575.42],
+        'L5': [1176.45]
+    },
+    'SBA': {
+        'L1': [1575.42]
+    },
+    'GLO': {
+        'L1': [round((1602 + i * 0.5625), 3) for i in range(-7, 7)]
+    },
+    'QZS': {
+        'L1': [1575.42],
+        'L5': [1176.45]
+    },
+    'BDS': {
+        'B1': [1561.098],
+        'B2a': [1176.45]
+    },
+    'GAL': {
+        'E1': [1575.42],
+        'E5a': [1176.45]
+    },
+    'NIC': {
+        'L5': [1176.45]
+    }
+}
+
+
+class RegexParseException(Exception):
+    pass
+
+
+class GnssSvidContainer:
+    """A class to hold the satellite svid information
+
+    Attributes:
+        used_in_fix: A dict contains unique svid used in fixing location
+        not_used_in_fix: A dict contains unique svid not used in fixing location
+    """
+
+    def __init__(self):
+        self.used_in_fix = defaultdict(set)
+        self.not_used_in_fix = defaultdict(set)
+
+    def add_satellite(self, gnss_status):
+        """Add satellite svid into container
+
+        According to the attributes gnss_status.used_in_fix
+            True: add svid into self.used_in_fix container
+            False: add svid into self.not_used_in_fix container
+
+        Args:
+            gnss_status: A GnssStatus object
+        """
+        key = f'{gnss_status.constellation}_{gnss_status.frequency_band}'
+        if gnss_status.used_in_fix:
+            self.used_in_fix[key].add(gnss_status.svid)
+        else:
+            self.not_used_in_fix[key].add(gnss_status.svid)
+
+
+class GnssStatus:
+    """GnssStatus object, it will create an obj with a raw gnssstatus line.
+
+    Attributes:
+        raw_message: (string) The raw log from GSPTool
+            example:
+                Fix: true Type: NIC SV: 4 C/No: 45.10782, 40.9 Elevation: 78.0
+                  Azimuth: 291.0
+                Signal: L5 Frequency: 1176.45 EPH: true ALM: false
+                Fix: false Type: GPS SV: 27 C/No: 34.728134, 30.5 Elevation:
+                  76.0 Azimuth: 15.0
+                Signal: L1 Frequency: 1575.42 EPH: true ALM: true
+        used_in_fix: (boolean) Whether or not this satellite info is used to fix
+          location
+        constellation: (string) The constellation type i.e. GPS
+        svid: (int) The unique id of the constellation
+        cn: (float) The C/No value from antenna
+        base_cn: (float) The C/No value from baseband
+        elev: (float) The value of elevation
+        azim: (float) The value of azimuth
+        frequency_band: (string) The frequency_type of the constellation i.e. L1
+          / L5
+    """
+
+    gnssstatus_re = (
+        r'Fix: (.*) Type: (.*) SV: (.*) C/No: (.*), (.*) '
+        r'Elevation: (.*) Azimuth: (.*) Signal: (.*) Frequency: (.*) EPH')
+    failures = []
+
+    def __init__(self, gnssstatus_raw):
+        status_res = re.search(self.gnssstatus_re, gnssstatus_raw)
+        if not status_res:
+            raise RegexParseException(f'Gnss raw msg parse fail:\n{gnssstatus_raw}\n'
+                                      f'Please check it manually.')
+        self.raw_message = gnssstatus_raw
+        self.used_in_fix = status_res.group(1).lower() == 'true'
+        self.constellation = status_res.group(2)
+        self.svid = int(status_res.group(3))
+        self.cn = float(status_res.group(4))
+        self.base_cn = float(status_res.group(5))
+        self.elev = float(status_res.group(6))
+        self.azim = float(status_res.group(7))
+        self.frequency_band = status_res.group(8)
+        self.carrier_frequency = float(status_res.group(9))
+
+    def validate_gnssstatus(self):
+        """A validate function for each property."""
+        self._validate_sv()
+        self._validate_cn()
+        self._validate_elev()
+        self._validate_azim()
+        self._validate_carrier_frequency()
+        if self.failures:
+            failure_info = '\n'.join(self.failures)
+            raise signals.TestFailure(
+                f'Gnsstatus validate failed:\n{self.raw_message}\n{failure_info}'
+            )
+
+    def _validate_sv(self):
+        """A validate function for SV ID."""
+        if not self.constellation in SVID_RANGE.keys():
+            raise signals.TestFailure(
+                f'Satellite identify fail: {self.constellation}')
+        for id_range in SVID_RANGE[self.constellation]:
+            if id_range[0] <= self.svid <= id_range[1]:
+                break
+        else:
+            fail_details = f'{self.constellation} ID {self.svid} not in SV Range'
+            self.failures.append(fail_details)
+
+    def _validate_cn(self):
+        """A validate function for CN value."""
+        if not 0 <= self.cn <= 63:
+            self.failures.append(f'Ant CN not in range: {self.cn}')
+        if not 0 <= self.base_cn <= 63:
+            self.failures.append(f'Base CN not in range: {self.base_cn}')
+
+    def _validate_elev(self):
+        """A validate function for Elevation (should between 0-90)."""
+        if not 0 <= self.elev <= 90:
+            self.failures.append(f'Elevation not in range: {self.elev}')
+
+    def _validate_azim(self):
+        """A validate function for Azimuth (should between 0-360)."""
+        if not 0 <= self.azim <= 360:
+            self.failures.append(f'Azimuth not in range: {self.azim}')
+
+    def _validate_carrier_frequency(self):
+        """A validate function for carrier frequency (should fall in below range).
+
+           'GPS': L1:1575.42, L5:1176.45
+           'SBA': L1:1575.42
+           'GLO': L1:Between 1598.0625 and 1605.375
+           'QZS': L1:1575.42, L5:1176.45
+           'BDS': B1:1561.098, B2a:1176.45
+           'GAL': E1:1575.42, E5a:1176.45
+           'NIC': L5:1176.45
+        """
+        if self.frequency_band in CARRIER_FREQUENCIES[
+                self.constellation].keys():
+            target_freq = CARRIER_FREQUENCIES[self.constellation][
+                self.frequency_band]
+        else:
+            raise signals.TestFailure(
+                f'Carrier frequency identify fail: {self.frequency_band}')
+        if not self.carrier_frequency in target_freq:
+            self.failures.append(
+                f'{self.constellation}_{self.frequency_band} carrier'
+                f'frequency not in range: {self.carrier_frequency}')
diff --git a/acts_tests/acts_contrib/test_utils/gnss/supl.py b/acts_tests/acts_contrib/test_utils/gnss/supl.py
new file mode 100644
index 0000000..f6c1bc6
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/gnss/supl.py
@@ -0,0 +1,71 @@
+import os
+import tempfile
+from xml.etree import ElementTree
+
+
+def set_supl_over_wifi_state(ad, turn_on):
+    """Enable / Disable supl over wifi features
+
+    Modify the gps xml file: /vendor/etc/gnss/gps.xml
+    Args:
+        ad: AndroidDevice object
+        turn_on: (bool) True -> enable / False -> disable
+    """
+    ad.adb.remount()
+    folder = tempfile.mkdtemp()
+    xml_path_on_host = os.path.join(folder, "gps.xml")
+    xml_path_on_device = "/vendor/etc/gnss/gps.xml"
+    ad.pull_files(xml_path_on_device, xml_path_on_host)
+
+    # register namespance to aviod adding ns0 into xml attributes
+    ElementTree.register_namespace("", "http://www.glpals.com/")
+    xml_tree = ElementTree.parse(xml_path_on_host)
+    root = xml_tree.getroot()
+    for node in root:
+        if "hal" in node.tag:
+            if turn_on:
+                _enable_supl_over_wifi(ad, node)
+            else:
+                _disable_supl_over_wifi(ad, node)
+    xml_tree.write(xml_path_on_host, xml_declaration=True, encoding="utf-8", method="xml")
+    ad.push_system_file(xml_path_on_host, xml_path_on_device)
+
+
+def _enable_supl_over_wifi(ad, node):
+    """Enable supl over wifi
+    Detail setting:
+        <hal
+            SuplDummyCellInfo="true"
+            SuplUseApn="false"
+            SuplUseApnNI="true"
+            SuplUseFwCellInfo="false"
+        />
+    Args:
+        ad: AndroidDevice object
+        node: ElementTree node
+    """
+    ad.log.info("Enable SUPL over wifi")
+    attributes = {"SuplDummyCellInfo": "true", "SuplUseApn": "false", "SuplUseApnNI": "true",
+                  "SuplUseFwCellInfo": "false"}
+    for key, value in attributes.items():
+        node.set(key, value)
+
+
+def _disable_supl_over_wifi(ad, node):
+    """Disable supl over wifi
+    Detail setting:
+        <hal
+            SuplUseApn="true"
+        />
+    Remove following setting
+        SuplDummyCellInfo="true"
+        SuplUseApnNI="true"
+        SuplUseFwCellInfo="false"
+    Args:
+        ad: AndroidDevice object
+        node: ElementTree node
+    """
+    ad.log.info("Disable SUPL over wifi")
+    for attri in ["SuplDummyCellInfo", "SuplUseApnNI", "SuplUseFwCellInfo"]:
+        node.attrib.pop(attri, None)
+    node.set("SuplUseApn", "true")
diff --git a/acts_tests/acts_contrib/test_utils/gnss/testtracker_util.py b/acts_tests/acts_contrib/test_utils/gnss/testtracker_util.py
new file mode 100644
index 0000000..43f7920
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/gnss/testtracker_util.py
@@ -0,0 +1,88 @@
+TEST_NAME_BY_TESTTRACKER_UUID = {
+    # GnssFunctionTest
+    "test_cs_first_fixed_system_server_restart": "8169c19d-ba2a-4fef-969b-87f793f4e699",
+    "test_cs_ttff_after_gps_service_restart": "247110d9-1c9e-429e-8e73-f16dd4a1ac74",
+    "test_gnss_one_hour_tracking": "b3d20ecb-3727-48ed-8a03-19694cc726c1",
+    "test_duty_cycle_function": "0bbfb818-da93-41d7-8d83-15bc53d8d2cf",
+    "test_gnss_init_error": "c661780d-4864-4292-9988-88f64448fb78",
+    "test_sap_valid_modes": "89bb8103-a3af-4953-8f07-e43c7e829bdd",
+    "test_network_location_provider_cell": "6f59d0f5-569c-4d52-990b-0042123b70ab",
+    "test_network_location_provider_wifi": "eec8b4bd-6990-4098-ad7a-acc19574bdee",
+    "test_gmap_location_report_battery_saver": "040556bf-1ffc-4db2-b2c5-19c4da19a256",
+    "test_gnss_ttff_cs_airplane_mode_on": "bc3d509c-0392-4af1-a0d0-68fd01167573",
+    "test_gnss_ttff_ws_airplane_mode_on": "dcafc69a-095e-4d58-8afb-5276c5763f4d",
+    "test_gnss_ttff_hs_airplane_mode_on": "090ea66c-19a1-4d0b-8c7e-dbc967597764",
+    "test_cs_ttff_in_weak_gnss_signal": "1980f980-3134-47b0-8dd8-9c5af6b742a6",
+    "test_ws_ttff_in_weak_gnss_signal": "d77c8db1-687b-48c5-8101-e4267da05995",
+    "test_hs_ttff_in_weak_gnss_signal": "dd4ccd93-9e49-45c2-a3ea-40781a40b820",
+    "test_quick_toggle_gnss_state": "36c14727-5de7-4589-ad1b-9119f9d9bb52",
+    "test_gnss_init_after_reboot": "79be8ab6-26cb-4d1a-b3d3-4e5681766901",
+    "test_host_gnssstatus_validation": "767c3024-0db4-4d40-9b03-f30355d72a06",
+    "test_onchip_gnssstatus_validation": "afb08722-2c79-46a6-80fd-9ede5018e384",
+    "test_location_update_after_resuming_from_deep_suspend": "140a7763-f42c-4917-a71f-fbc0626c1609",
+    "test_location_mode_in_battery_saver_with_screen_off": "04b529f1-a99d-4b18-9bba-41e008249f7a",
+    "test_measure_adr_rate_after_10_mins_tracking": "7ebf3b52-229a-4eaf-bbff-7c527e4a1d7c",
+    "test_hal_crashing_should_resume_tracking": "0aee4450-edce-4e1a-8744-70d8c01937b0",
+    "test_power_save_mode_should_apply_latest_measurement_setting": "59a14da2-40df-4106-a190-dcbcd2e877e0",
+    # GnssConcurrencyTest
+    "test_gnss_concurrency_location_1_chre_1": "cbd9ff54-4405-44a4-ac20-de33278406d1",
+    "test_gnss_concurrency_location_1_chre_8": "ab56cb47-384e-4269-b2d8-6e80ce066de2",
+    "test_gnss_concurrency_location_15_chre_8": "e64fa984-6219-43dd-96d6-d4141b2da1cd",
+    "test_gnss_concurrency_location_61_chre_1": "217f3ab6-25c9-4092-8fe1-f4e4199d60c6",
+    "test_gnss_concurrency_location_61_chre_10": "c32ca948-0414-4529-98d0-8351b5f31bab",
+    "test_gnss_chre_1": "9dae57f3-70f9-4328-a448-925da88725ac",
+    "test_gnss_chre_8": "a8c8f7fa-4dfd-42d8-ac6a-c3e3f186e317",
+    "test_variable_interval_via_chre": "53b161e5-335e-44a7-ae2e-eae7464a2b37",
+    "test_variable_interval_via_framework": "6b525afa-1427-4a99-906f-bc0aab6d4d30",
+    "test_gps_engine_switching_host_to_onchip": "07e0e138-4966-4307-b600-0521e626b967",
+    "test_gps_engine_switching_onchip_to_host": "564a229e-e784-43af-b430-4ab14656cfdc",
+    "test_mcu_cs_ttff": "736da33a-a976-44b6-93b3-bcfd847dd03d",
+    "test_mcu_ws_ttff": "e3457e35-9872-4677-8170-bc30d84798c0",
+    "test_mcu_hs_ttff": "0e1ce60d-e257-4dc5-b927-7ae97c8386b6",
+    # GnssBroadcomConfigurationTest
+    "test_gps_logenabled_setting": "d1310171-1641-4fa2-8802-cca7ce33bbd4",
+    "test_gps_supllogenable_setting": "ebe30341-4097-4e2c-b104-0c592f1f9e83",
+    "test_lhe_setting": "099aea19-5078-447c-925f-01a702624884",
+    # GnssSuplTest
+    "test_supl_capabilities": "6c794396-46e8-4674-8985-49a7b3059372",
+    "test_supl_ttff_cs": "ae8b6d54-bdd6-44a1-b1fa-4e90e0318080",
+    "test_supl_ttff_ws": "65f25e0b-c6d0-47c5-ab1f-0b02b621411d",
+    "test_supl_ttff_hs": "a2267586-97e9-465c-8d3a-22882c8671e7",
+    "test_cs_ttff_supl_over_wifi_with_airplane_mode_on": "4b2882f8-2966-4b44-9a31-37318beb84bf",
+    "test_ws_ttff_supl_over_wifi_with_airplane_mode_on": "a7f77afe-c82e-4b1b-ae54-e3fea17bf721",
+    "test_hs_ttff_supl_over_wifi_with_airplane_mode_on": "bc9de22f-90a0-4f2b-8052-cb4529f745e3",
+    "test_ttff_gla_on": "06aa85a2-7c3a-453a-b765-dc9ea6ee6b9b",
+    "test_ttff_gla_off": "36347b6e-d03e-4773-82bf-2e12d4f4dd0d",
+    # GnssVendorFeaturesTest
+    "test_xtra_ttff_cs_mobile_data": "da0bf0a1-d635-4942-808a-30070cfb2c78",
+    "test_xtra_ttff_ws_mobile_data": "95f17477-c88e-4663-a0ab-c09dc1706f75",
+    "test_xtra_ttff_hs_mobile_data": "bf13e2e4-79c9-4769-9dfd-81b085112744",
+    "test_xtra_ttff_cs_wifi": "5c0f95d2-7c76-45ca-95c8-304c742e0c82",
+    "test_xtra_ttff_ws_wifi": "507b2da1-c58d-4bca-810b-b274082a21c4",
+    "test_xtra_ttff_hs_wifi": "69d1998a-dd78-46a9-904d-d28f07dc3ef2",
+    "test_xtra_download_mobile_data": "d260a510-941a-48c7-a545-d7239f8f03dc",
+    "test_xtra_download_wifi": "e1dec4d2-4a85-4680-92df-57c972c084aa",
+    "test_lto_download_after_reboot": "579d249e-d533-4979-915f-b3a7d847546e",
+    "test_ws_with_assist": "938cbc1f-0374-473c-b4c1-1b4af734f16a",
+    "test_cs_with_assist": "9eae7b7d-9356-4fd8-bc18-f9d93aa0a92b",
+    # GnssWearableTetherFunctionTest
+    "test_flp_ttff_cs": "6dec9502-7e74-4590-b174-be822ceefcdb",
+    "test_flp_ttff_ws": "ddb8f09d-4757-42ae-9707-1fdb38187d1f",
+    "test_flp_ttff_hs": "19a997da-7c36-4fef-bf68-497d7c21163f",
+    "test_tracking_during_bt_disconnect_resume": "c9e26620-e518-4c7d-afcc-f81f6d3971bb",
+    "test_oobe_first_fix": "432e799c-3d02-46de-84d5-d5a22feceef8",
+    "test_oobe_first_fix_with_network_connection": "66264dac-50d0-4bc0-be72-6dbe9159587b",
+    "test_far_start_ttff": "4693b424-1a24-4002-b51d-df6b6bb91830",
+}
+
+def log_testtracker_uuid(ad, current_test_name):
+    """Logs testtracker uuid for the current test case.
+
+    Args:
+        ad: Target AndroidDevice object.
+        current_test_name: Current test name used to map testtracker uuid.
+    """
+    current_test_uuid = TEST_NAME_BY_TESTTRACKER_UUID.get(
+        current_test_name, None)
+    if current_test_uuid:
+        ad.log.info(f"TestResult mobly_uid {current_test_uuid}")
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/net/arduino_test_utils.py b/acts_tests/acts_contrib/test_utils/net/arduino_test_utils.py
index 595512c..29eec9b 100644
--- a/acts_tests/acts_contrib/test_utils/net/arduino_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/net/arduino_test_utils.py
@@ -14,13 +14,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import logging
-import time
-import pprint
-
-from enum import IntEnum
-from queue import Empty
-
 from acts import asserts
 from acts import signals
 from acts import utils
@@ -35,6 +28,7 @@
 SSID = wutils.WifiEnums.SSID_KEY
 PWD = wutils.WifiEnums.PWD_KEY
 
+
 def connect_wifi(wd, network=None):
     """Connect wifi on arduino wifi dongle
 
@@ -53,6 +47,7 @@
     ping_status = wd.ping_status()
     asserts.assert_true(ping_status, "Failed to connect to internet")
 
+
 def disconnect_wifi(wd):
     """Disconnect wifi on arduino wifi dongle
 
diff --git a/acts_tests/acts_contrib/test_utils/net/ipsec_test_utils.py b/acts_tests/acts_contrib/test_utils/net/ipsec_test_utils.py
index 5f383ee..d9dc553 100644
--- a/acts_tests/acts_contrib/test_utils/net/ipsec_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/net/ipsec_test_utils.py
@@ -15,23 +15,22 @@
 
 import binascii
 import os
-import random
 import re
-import threading
-import time
 
 from acts_contrib.test_utils.net import connectivity_const as cconst
 from acts import asserts
 
 PKTS = 5
 
+
 def make_key(len_bits):
     asserts.assert_true(
         len_bits % 8 == 0, "Unexpected key length. Should be a multiple "
         "of 8, got %s" % len_bits)
-    return binascii.hexlify(os.urandom(int(len_bits/8))).decode()
+    return binascii.hexlify(os.urandom(int(len_bits / 8))).decode()
 
-def allocate_spis(ad, ip_a, ip_b, in_spi = None, out_spi = None):
+
+def allocate_spis(ad, ip_a, ip_b, in_spi=None, out_spi=None):
     """ Allocate in and out SPIs for android device
 
     Args:
@@ -55,6 +54,7 @@
     asserts.assert_true(in_spi and out_spi, "Failed to allocate SPIs")
     return [in_spi_key, out_spi_key]
 
+
 def release_spis(ad, spis):
     """ Destroy SPIs
 
@@ -67,6 +67,7 @@
         spi = ad.droid.ipSecGetSecurityParameterIndex(spi_key)
         asserts.assert_true(not spi, "Failed to release SPI")
 
+
 def create_transport_mode_transforms(ad,
                                      spis,
                                      ip_a,
@@ -92,17 +93,18 @@
       List of In and Out Transforms
     """
     in_transform = ad.droid.ipSecCreateTransportModeTransform(
-        crypt_algo, crypt_key, auth_algo, auth_key, trunc_bit, spis[0],
-        ip_b, udp_encap_sock)
+        crypt_algo, crypt_key, auth_algo, auth_key, trunc_bit, spis[0], ip_b,
+        udp_encap_sock)
     ad.log.info("In Transform: %s" % in_transform)
     out_transform = ad.droid.ipSecCreateTransportModeTransform(
-        crypt_algo, crypt_key, auth_algo, auth_key, trunc_bit, spis[1],
-        ip_a, udp_encap_sock)
+        crypt_algo, crypt_key, auth_algo, auth_key, trunc_bit, spis[1], ip_a,
+        udp_encap_sock)
     ad.log.info("Out Transform: %s" % out_transform)
     asserts.assert_true(in_transform and out_transform,
                         "Failed to create transforms")
     return [in_transform, out_transform]
 
+
 def destroy_transport_mode_transforms(ad, transforms):
     """ Destroy transforms on the device
 
@@ -116,6 +118,7 @@
         ad.log.info("Transform status: %s" % status)
         asserts.assert_true(not status, "Failed to destroy transform")
 
+
 def apply_transport_mode_transforms_file_descriptors(ad, fd, transforms):
     """ Apply transpot mode transform to FileDescriptor object
 
@@ -135,6 +138,7 @@
     ip_xfrm_policy = ad.adb.shell("ip -s xfrm policy")
     ad.log.info("XFRM POLICY:\n%s\n" % ip_xfrm_policy)
 
+
 def remove_transport_mode_transforms_file_descriptors(ad, fd):
     """ Remove transport mode transform from FileDescriptor object
 
@@ -145,6 +149,7 @@
     status = ad.droid.ipSecRemoveTransportModeTransformsFileDescriptor(fd)
     asserts.assert_true(status, "Failed to remove transform")
 
+
 def apply_transport_mode_transforms_datagram_socket(ad, socket, transforms):
     """ Apply transport mode transform to DatagramSocket object
 
@@ -163,6 +168,7 @@
     ip_xfrm_state = ad.adb.shell("ip -s xfrm state")
     ad.log.info("XFRM STATE:\n%s\n" % ip_xfrm_state)
 
+
 def remove_transport_mode_transforms_datagram_socket(ad, socket):
     """ Remove transport mode transform from DatagramSocket object
 
@@ -173,6 +179,7 @@
     status = ad.droid.ipSecRemoveTransportModeTransformsDatagramSocket(socket)
     asserts.assert_true(status, "Failed to remove transform")
 
+
 def apply_transport_mode_transforms_socket(ad, socket, transforms):
     """ Apply transport mode transform to Socket object
 
@@ -191,6 +198,7 @@
     ip_xfrm_state = ad.adb.shell("ip -s xfrm state")
     ad.log.info("XFRM STATE:\n%s\n" % ip_xfrm_state)
 
+
 def remove_transport_mode_transforms_socket(ad, socket):
     """ Remove transport mode transform from Socket object
 
@@ -201,6 +209,7 @@
     status = ad.droid.ipSecRemoveTransportModeTransformsSocket(socket)
     asserts.assert_true(status, "Failed to remove transform")
 
+
 def verify_esp_packets(ads):
     """ Verify that encrypted ESP packets are sent
 
@@ -218,21 +227,26 @@
                 break
         asserts.assert_true(esp_pkts, "Could not find ESP pkts")
 
+
 def generate_random_crypt_auth_combo():
     """ Generate every possible combination of crypt and auth keys,
         auth algo, trunc bits supported by IpSecManager
     """
     crypt_key_length = [128, 192, 256]
-    auth_method_key = { cconst.AUTH_HMAC_MD5 : 128,
-                        cconst.AUTH_HMAC_SHA1 : 160,
-                        cconst.AUTH_HMAC_SHA256 : 256,
-                        cconst.AUTH_HMAC_SHA384 : 384,
-                        cconst.AUTH_HMAC_SHA512 : 512 }
-    auth_method_trunc = { cconst.AUTH_HMAC_MD5 : list(range(96, 136, 8)),
-                          cconst.AUTH_HMAC_SHA1 : list(range(96, 168, 8)),
-                          cconst.AUTH_HMAC_SHA256 : list(range(96, 264, 8)),
-                          cconst.AUTH_HMAC_SHA384 : list(range(192, 392, 8)),
-                          cconst.AUTH_HMAC_SHA512 : list(range(256, 520, 8)) }
+    auth_method_key = {
+        cconst.AUTH_HMAC_MD5: 128,
+        cconst.AUTH_HMAC_SHA1: 160,
+        cconst.AUTH_HMAC_SHA256: 256,
+        cconst.AUTH_HMAC_SHA384: 384,
+        cconst.AUTH_HMAC_SHA512: 512
+    }
+    auth_method_trunc = {
+        cconst.AUTH_HMAC_MD5: list(range(96, 136, 8)),
+        cconst.AUTH_HMAC_SHA1: list(range(96, 168, 8)),
+        cconst.AUTH_HMAC_SHA256: list(range(96, 264, 8)),
+        cconst.AUTH_HMAC_SHA384: list(range(192, 392, 8)),
+        cconst.AUTH_HMAC_SHA512: list(range(256, 520, 8))
+    }
     return_list = []
     for c in crypt_key_length:
         for k in auth_method_key.keys():
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 c6086af..c789686 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
@@ -34,8 +34,6 @@
 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.config import conf
-from scapy.compat import plain_str
 
 VPN_CONST = cconst.VpnProfile
 VPN_TYPE = cconst.VpnProfileType
@@ -572,6 +570,8 @@
         A list of the latest network interfaces. For example:
         ['cvd-ebr', ..., 'eno1', 'enx4afa19a8dde1', 'lo', 'wlxd03745d68d88']
     """
+    from scapy.config import conf
+    from scapy.compat import plain_str
 
     # Get ifconfig output
     result = job.run([conf.prog.ifconfig])
@@ -619,6 +619,8 @@
     Args:
         iface: network interface that need to enable
     """
+    from scapy.compat import plain_str
+
     result = job.run("sudo ifconfig %s up" % (iface), ignore_status=True)
     if result.exit_status:
         raise asserts.fail(
diff --git a/acts_tests/acts_contrib/test_utils/net/socket_test_utils.py b/acts_tests/acts_contrib/test_utils/net/socket_test_utils.py
index b9a6bdf..1192b4c 100644
--- a/acts_tests/acts_contrib/test_utils/net/socket_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/net/socket_test_utils.py
@@ -14,7 +14,6 @@
 #   limitations under the License.
 
 import queue
-import re
 import threading
 import time
 
@@ -23,8 +22,9 @@
 
 MSG = "Test message "
 PKTS = 5
-
 """ Methods for android.system.Os based sockets """
+
+
 def open_android_socket(ad, domain, sock_type, ip, port):
     """ Open TCP or UDP using android.system.Os class
 
@@ -43,6 +43,7 @@
     asserts.assert_true(fd_key, "Failed to open socket")
     return fd_key
 
+
 def close_android_socket(ad, fd_key):
     """ Close socket
 
@@ -53,12 +54,9 @@
     status = ad.droid.closeSocket(fd_key)
     asserts.assert_true(status, "Failed to close socket")
 
-def listen_accept_android_socket(client,
-                                 server,
-                                 client_fd,
-                                 server_fd,
-                                 server_ip,
-                                 server_port):
+
+def listen_accept_android_socket(client, server, client_fd, server_fd,
+                                 server_ip, server_port):
     """ Listen, accept TCP sockets
 
     Args:
@@ -75,12 +73,9 @@
     asserts.assert_true(sock, "Failed to accept socket")
     return sock
 
-def send_recv_data_android_sockets(client,
-                                   server,
-                                   client_fd,
-                                   server_fd,
-                                   server_ip,
-                                   server_port):
+
+def send_recv_data_android_sockets(client, server, client_fd, server_fd,
+                                   server_ip, server_port):
     """ Send TCP or UDP data over android os sockets from client to server.
         Verify that server received the data.
 
@@ -95,7 +90,7 @@
     send_list = []
     recv_list = []
 
-    for _ in range(1, PKTS+1):
+    for _ in range(1, PKTS + 1):
         msg = MSG + " %s" % _
         send_list.append(msg)
         client.log.info("Sending message: %s" % msg)
@@ -108,7 +103,10 @@
     asserts.assert_true(send_list and recv_list and send_list == recv_list,
                         "Send and recv information is incorrect")
 
+
 """ Methods for java.net.DatagramSocket based sockets """
+
+
 def open_datagram_socket(ad, ip, port):
     """ Open datagram socket
 
@@ -125,6 +123,7 @@
     asserts.assert_true(socket_key, "Failed to open datagram socket")
     return socket_key
 
+
 def close_datagram_socket(ad, socket_key):
     """ Close datagram socket
 
@@ -134,12 +133,9 @@
     status = ad.droid.closeDatagramSocket(socket_key)
     asserts.assert_true(status, "Failed to close datagram socket")
 
-def send_recv_data_datagram_sockets(client,
-                                    server,
-                                    client_sock,
-                                    server_sock,
-                                    server_ip,
-                                    server_port):
+
+def send_recv_data_datagram_sockets(client, server, client_sock, server_sock,
+                                    server_ip, server_port):
     """ Send data over datagram socket from dut_a to dut_b.
         Verify that dut_b received the data.
 
@@ -154,13 +150,11 @@
     send_list = []
     recv_list = []
 
-    for _ in range(1, PKTS+1):
+    for _ in range(1, PKTS + 1):
         msg = MSG + " %s" % _
         send_list.append(msg)
         client.log.info("Sending message: %s" % msg)
-        client.droid.sendDataOverDatagramSocket(client_sock,
-                                                msg,
-                                                server_ip,
+        client.droid.sendDataOverDatagramSocket(client_sock, msg, server_ip,
                                                 server_port)
         recv_msg = server.droid.recvDataOverDatagramSocket(server_sock)
         server.log.info("Received message: %s" % recv_msg)
@@ -170,28 +164,26 @@
     asserts.assert_true(send_list and recv_list and send_list == recv_list,
                         "Send and recv information is incorrect")
 
+
 """ Utils methods for java.net.Socket based sockets """
+
+
 def _accept_socket(server, server_ip, server_port, server_sock, q):
     sock = server.droid.acceptTcpSocket(server_sock)
     server.log.info("Server socket: %s" % sock)
     q.put(sock)
 
+
 def _client_socket(client, server_ip, server_port, client_ip, client_port, q):
     time.sleep(0.5)
-    sock = client.droid.openTcpSocket(server_ip,
-                                      server_port,
-                                      client_ip,
+    sock = client.droid.openTcpSocket(server_ip, server_port, client_ip,
                                       client_port)
     client.log.info("Client socket: %s" % sock)
     q.put(sock)
 
-def open_connect_socket(client,
-                        server,
-                        client_ip,
-                        server_ip,
-                        client_port,
-                        server_port,
-                        server_sock):
+
+def open_connect_socket(client, server, client_ip, server_ip, client_port,
+                        server_port, server_sock):
     """ Open tcp socket and connect to server
 
     Args:
@@ -208,12 +200,12 @@
     """
     sq = queue.Queue()
     cq = queue.Queue()
-    s = threading.Thread(target = _accept_socket,
-                         args = (server, server_ip, server_port, server_sock,
-                                 sq))
-    c = threading.Thread(target = _client_socket,
-                         args = (client, server_ip, server_port, client_ip,
-                                 client_port, cq))
+    s = threading.Thread(target=_accept_socket,
+                         args=(server, server_ip, server_port, server_sock,
+                               sq))
+    c = threading.Thread(target=_client_socket,
+                         args=(client, server_ip, server_port, client_ip,
+                               client_port, cq))
     s.start()
     c.start()
     c.join()
@@ -225,6 +217,7 @@
 
     return client_sock, server_sock
 
+
 def open_server_socket(server, server_ip, server_port):
     """ Open tcp server socket
 
@@ -238,6 +231,7 @@
     asserts.assert_true(sock, "Failed to open server socket")
     return sock
 
+
 def close_socket(ad, socket):
     """ Close socket
 
@@ -248,6 +242,7 @@
     status = ad.droid.closeTcpSocket(socket)
     asserts.assert_true(status, "Failed to socket")
 
+
 def close_server_socket(ad, socket):
     """ Close server socket
 
@@ -258,6 +253,7 @@
     status = ad.droid.closeTcpServerSocket(socket)
     asserts.assert_true(status, "Failed to socket")
 
+
 def shutdown_socket(ad, socket):
     """ Shutdown socket
 
@@ -270,6 +266,7 @@
     status = ad.droid.shutdownFileDescriptor(fd)
     asserts.assert_true(status, "Failed to shutdown socket")
 
+
 def send_recv_data_sockets(client, server, client_sock, server_sock):
     """ Send data over TCP socket from client to server.
         Verify that server received the data
@@ -283,7 +280,7 @@
     send_list = []
     recv_list = []
 
-    for _ in range(1, PKTS+1):
+    for _ in range(1, PKTS + 1):
         msg = MSG + " %s" % _
         send_list.append(msg)
         client.log.info("Sending message: %s" % msg)
diff --git a/acts_tests/acts_contrib/test_utils/power/PowerBaseTest.py b/acts_tests/acts_contrib/test_utils/power/PowerBaseTest.py
index 11094fb..7af1e12 100644
--- a/acts_tests/acts_contrib/test_utils/power/PowerBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/power/PowerBaseTest.py
@@ -27,6 +27,7 @@
 from acts import base_test
 from acts import utils
 from acts.metrics.loggers.blackbox import BlackboxMetricLogger
+from acts.controllers.adb_lib.error import AdbError
 from acts_contrib.test_utils.power.loggers.power_metric_logger import PowerMetricLogger
 from acts_contrib.test_utils.power import plot_utils
 
@@ -35,6 +36,8 @@
 THRESHOLD_TOLERANCE_DEFAULT = 0.2
 GET_FROM_PHONE = 'get_from_dut'
 GET_FROM_AP = 'get_from_ap'
+GET_PROPERTY_HARDWARE_PLATFORM = 'getprop ro.boot.hardware.platform'
+POWER_STATS_DUMPSYS_CMD = 'dumpsys android.hardware.power.stats.IPowerStats/default delta'
 PHONE_BATTERY_VOLTAGE_DEFAULT = 4.2
 MONSOON_MAX_CURRENT = 8.0
 DEFAULT_MONSOON_FREQUENCY = 500
@@ -76,6 +79,7 @@
         self.dut = None
         self.power_logger = PowerMetricLogger.for_test_case()
         self.power_monitor = None
+        self.odpm_folder = None
 
     @property
     def final_test(self):
@@ -150,13 +154,23 @@
                                iperf_duration=None,
                                pass_fail_tolerance=THRESHOLD_TOLERANCE_DEFAULT,
                                mon_voltage=PHONE_BATTERY_VOLTAGE_DEFAULT,
-                               ap_dtim_period=None)
+                               ap_dtim_period=None,
+                               bits_root_rail_csv_export=False)
 
         # Setup the must have controllers, phone and monsoon
         self.dut = self.android_devices[0]
         self.mon_data_path = os.path.join(self.log_path, 'Monsoon')
         os.makedirs(self.mon_data_path, exist_ok=True)
 
+        # Make odpm path for P21 or later
+        platform = self.dut.adb.shell(GET_PROPERTY_HARDWARE_PLATFORM)
+        self.log.info('The hardware platform is {}'.format(platform))
+        if platform.startswith('gs'):
+            self.odpm_folder = os.path.join(self.log_path, 'odpm')
+            os.makedirs(self.odpm_folder, exist_ok=True)
+            self.log.info('For P21 or later, create odpm folder {}'.format(
+                self.odpm_folder))
+
         # Initialize the power monitor object that will be used to measure
         self.initialize_power_monitor()
 
@@ -279,6 +293,26 @@
     def on_pass(self, test_name, begin_time):
         self.power_logger.set_pass_fail_status('PASS')
 
+    def dut_save_odpm(self, tag):
+        """Dumpsys ODPM data and save it to self.odpm_folder.
+
+        Args:
+            tag: the moment of save ODPM data
+        """
+        odpm_file_name = '{}.{}.dumpsys_odpm_{}.txt'.format(
+            self.__class__.__name__,
+            self.current_test_name,
+            tag)
+        odpm_file_path = os.path.join(self.odpm_folder, odpm_file_name)
+
+        try:
+            stats = self.dut.adb.shell(POWER_STATS_DUMPSYS_CMD)
+            with open(odpm_file_path, 'w') as f:
+                f.write(stats)
+        except AdbError as e:
+            self.log.warning('Odpm data with tag {} did not save due to adb '
+                             'error {}'.format(e))
+
     def dut_rockbottom(self):
         """Set the dut to rockbottom state
 
@@ -464,6 +498,11 @@
         # Start the power measurement using monsoon.
         self.dut.stop_services()
         time.sleep(1)
+
+        # P21 or later device, save the odpm data before power measurement
+        if self.odpm_folder:
+            self.dut_save_odpm('before')
+
         self.power_monitor.disconnect_usb()
         measurement_args = dict(duration=self.mon_info.duration,
                                 measure_after_seconds=self.mon_info.offset,
@@ -473,9 +512,15 @@
                                    start_time=device_to_host_offset,
                                    monsoon_output_path=data_path)
         self.power_monitor.release_resources()
+        self.collect_raw_data_samples()
         self.power_monitor.connect_usb()
         self.dut.wait_for_boot_completion()
         time.sleep(10)
+
+        # For P21 or later device, save the odpm data after power measurement
+        if self.odpm_folder:
+            self.dut_save_odpm('after')
+
         self.dut.start_services()
 
         return self.power_monitor.get_waveform(file_path=data_path)
@@ -510,3 +555,9 @@
             self.log.warning('Cannot get iperf result. Setting to 0')
             throughput = 0
         return throughput
+
+    def collect_raw_data_samples(self):
+        if hasattr(self, 'bitses') and self.bits_root_rail_csv_export:
+            path = os.path.join(os.path.dirname(self.mon_info.data_path),
+                                'Kibble')
+            self.power_monitor.get_bits_root_rail_csv_export(path, self.test_name)
diff --git a/acts_tests/acts_contrib/test_utils/power/PowerGTWGnssBaseTest.py b/acts_tests/acts_contrib/test_utils/power/PowerGTWGnssBaseTest.py
index baedb7e..201f17f 100644
--- a/acts_tests/acts_contrib/test_utils/power/PowerGTWGnssBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/power/PowerGTWGnssBaseTest.py
@@ -41,6 +41,7 @@
         self.set_xtra_data()
 
     def setup_test(self):
+        gutils.log_current_epoch_time(self.ad, "test_start_time")
         super().setup_test()
         # Enable DPO
         self.enable_DPO(True)
@@ -53,6 +54,7 @@
         begin_time = utils.get_current_epoch_time()
         self.ad.take_bug_report(self.test_name, begin_time)
         gutils.get_gnss_qxdm_log(self.ad, self.qdsp6m_path)
+        gutils.log_current_epoch_time(self.ad, "test_end_time")
 
     def set_xtra_data(self):
         gutils.disable_xtra_throttle(self.ad)
diff --git a/acts_tests/acts_contrib/test_utils/power/PowerWiFiBaseTest.py b/acts_tests/acts_contrib/test_utils/power/PowerWiFiBaseTest.py
index 4a93afb..80113df 100644
--- a/acts_tests/acts_contrib/test_utils/power/PowerWiFiBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/power/PowerWiFiBaseTest.py
@@ -14,6 +14,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+from retry import retry
+
 import acts_contrib.test_utils.power.PowerBaseTest as PBT
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils
@@ -50,8 +52,17 @@
         if self.iperf_duration:
             self.mon_duration = self.iperf_duration - self.mon_offset - IPERF_TAIL
             self.mon_info = self.create_monsoon_info()
+        try:
+          self._set_country_code()
+        except Exception as e:
+          self.log.warning('error in set country code with %s', e)
+          country_code = self.dut.droid.wifiGetCountryCode()
+          self.log.info('the country code is %s', country_code)
 
-        wutils.set_wifi_country_code(self.dut, 'US')
+    @retry(tries=5, delay=10)
+    def _set_country_code(self):
+      wutils.wifi_toggle_state(self.dut, True)
+      wutils.set_wifi_country_code(self.dut, 'US')
 
     def teardown_test(self):
         """Tear down necessary objects after test case is finished.
@@ -180,4 +191,3 @@
 
         wutils.reset_wifi(self.dut)
         wutils.wifi_toggle_state(self.dut, False)
-
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_idle_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_idle_power_test.py
index 72cb7ee..a834a82 100644
--- a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_idle_power_test.py
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_idle_power_test.py
@@ -35,6 +35,9 @@
             filter_results: when True the reported result is filtered to only
                 samples where average power was below a certain threshold.
         """
+        # disable data for idle test
+        self.dut.adb.shell('service call phone 39')
+        self.dut.adb.shell('svc data disable')
 
         idle_wait_time = self.simulation.rrc_sc_timer + 30
 
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_pdcch_power_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_pdcch_power_test.py
index c071c5a..6f4f041 100644
--- a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_pdcch_power_test.py
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_pdcch_power_test.py
@@ -13,6 +13,7 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
+import os
 
 import acts_contrib.test_utils.power.cellular.cellular_power_base_test as base_test
 
@@ -21,6 +22,7 @@
     """ PDCCH only power test.
 
     In this test the UE is only listening and decoding the PDCCH channel. """
+
     def power_pdcch_test(self):
         """ Measures power during PDCCH only.
 
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_power_base_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_power_base_test.py
index 5d3c913..f2ebea9 100644
--- a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_power_base_test.py
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_power_base_test.py
@@ -13,11 +13,13 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
+import json
 import os
 
 import acts_contrib.test_utils.power.PowerBaseTest as PBT
 import acts_contrib.test_utils.cellular.cellular_base_test as CBT
 from acts_contrib.test_utils.power import plot_utils
+from acts import context
 
 
 class PowerCellularLabBaseTest(CBT.CellularBaseTest, PBT.PowerBaseTest):
@@ -39,6 +41,21 @@
         """ Turn screen on before starting a test. """
 
         super().setup_test()
+        try:
+            # Save a json file for device info
+            path = os.path.join(self.log_path, 'device_info.json')
+
+            self.log.info('save the device info to {}'.format(path))
+            baseband = self.dut.adb.getprop('gsm.version.baseband')
+            self.dut.add_device_info('baseband', baseband)
+            with open(path, 'w') as f:
+                json.dump(
+                    self.dut.device_info,
+                    f,
+                    indent=2,
+                    sort_keys=True)
+        except Exception as e:
+            self.log.error('error in saving device_info: {}'.format(e))
 
         # Make the device go to sleep
         self.dut.droid.goToSleepNow()
@@ -98,8 +115,12 @@
 
         path = os.path.join(self.log_path, self.RESULTS_SUMMARY_FILENAME)
 
-        with open(path, 'w') as csvfile:
-            csvfile.write('test,avg_power')
+        # To avoid the test overwrite each other, open file with 'a' option
+        csvfile_exist = os.path.exists(path)
+
+        with open(path, 'a') as csvfile:
+            if not csvfile_exist:
+                csvfile.write('test,avg_power')
             for test_name, value in self.power_results.items():
                 csvfile.write('\n{},{}'.format(test_name, value))
 
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/cellular_power_preset_base_test.py b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_power_preset_base_test.py
new file mode 100644
index 0000000..15043d4
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/cellular_power_preset_base_test.py
@@ -0,0 +1,414 @@
+import os
+from typing import Optional, List
+import time
+from acts import context
+from acts import signals
+from acts.controllers.cellular_lib import AndroidCellularDut
+import acts_contrib.test_utils.power.cellular.cellular_power_base_test as PWCEL
+
+# TODO: b/261639867
+class AtUtil():
+    """Util class for sending at command.
+
+    Attributes:
+        dut: AndroidDevice controller object.
+    """
+    ADB_CMD_DISABLE_TXAS = 'am instrument -w -e request at+googtxas=2 -e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
+
+    def __init__(self, dut, log) -> None:
+        self.dut = dut
+        self.log = log
+
+    # TODO: to be remove when b/261639867 complete,
+    # and we are using parent method.
+    def send(self, cmd: str,) -> Optional[str]:
+        res = str(self.dut.adb.shell(cmd))
+        self.log.info(f'cmd sent: {cmd}')
+        self.log.info(f'response: {res}')
+        if 'SUCCESS' in res:
+            self.log.info('Command executed.')
+        else:
+            self.log.error('Fail to executed command.')
+        return res
+
+    def lock_LTE(self):
+        adb_enable_band_lock_lte = r'am instrument -w -e request at+GOOGSETNV=\"!SAEL3.Manual.Band.Select\ Enb\/\ Dis\",00,\"01\" -e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
+        adb_set_band_lock_mode_lte = r'am instrument -w -e request at+GOOGSETNV=\"NASU.SIPC.NetworkMode.ManualMode\",0,\"0D\" -e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
+        adb_set_band_lock_bitmap_0 = r'am instrument -w -e request at+GOOGSETNV=\"!SAEL3.Manual.Enabled.RFBands.BitMap\",0,\"09,00,00,00,00,00,00,00\" -e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
+        adb_set_band_lock_bitmap_1 = r'am instrument -w -e request at+GOOGSETNV=\"!SAEL3.Manual.Enabled.RFBands.BitMap\",1,\"00,00,00,00,00,00,00,00\" -e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
+        adb_set_band_lock_bitmap_2 = r'am instrument -w -e request at+GOOGSETNV=\"!SAEL3.Manual.Enabled.RFBands.BitMap\",2,\"00,00,00,00,00,00,00,00\" -e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
+        adb_set_band_lock_bitmap_3 = r'am instrument -w -e request at+GOOGSETNV=\"!SAEL3.Manual.Enabled.RFBands.BitMap\",3,\"00,00,00,00,00,00,00,00\" -e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
+
+        # enable lte
+        self.send(adb_enable_band_lock_lte)
+        time.sleep(2)
+
+        # OD is for NR/LTE in 4412 menu
+        self.send(adb_set_band_lock_mode_lte)
+        time.sleep(2)
+
+        # lock to B1 and B4
+        self.send(adb_set_band_lock_bitmap_0)
+        time.sleep(2)
+        self.send(adb_set_band_lock_bitmap_1)
+        time.sleep(2)
+        self.send(adb_set_band_lock_bitmap_2)
+        time.sleep(2)
+        self.send(adb_set_band_lock_bitmap_3)
+        time.sleep(2)
+
+    def clear_lock_band(self):
+        adb_set_band_lock_mode_auto = r'am instrument -w -e request at+GOOGSETNV=\"NASU.SIPC.NetworkMode.ManualMode\",0,\"03\" -e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
+        adb_disable_band_lock_lte = r'am instrument -w -e request at+GOOGSETNV=\"!SAEL3.Manual.Band.Select\ Enb\/\ Dis\",0,\"00\" -e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
+
+        # band lock mode auto
+        self.send(adb_set_band_lock_mode_auto)
+        time.sleep(2)
+
+        # disable band lock lte
+        self.send(adb_disable_band_lock_lte)
+        time.sleep(2)
+
+    def disable_txas(self):
+        cmd = self.ADB_CMD_DISABLE_TXAS
+        response = self.send(cmd)
+        return 'OK' in response
+
+class PowerCellularPresetLabBaseTest(PWCEL.PowerCellularLabBaseTest):
+    # Key for ODPM report
+    ODPM_ENERGY_TABLE_NAME = 'PowerStats HAL 2.0 energy meter'
+    ODPM_MODEM_CHANNEL_NAME = '[VSYS_PWR_MODEM]:Modem'
+
+    # Key for custom_property in Sponge
+    CUSTOM_PROP_KEY_BUILD_ID = 'build_id'
+    CUSTOM_PROP_KEY_INCR_BUILD_ID = 'incremental_build_id'
+    CUSTOM_PROP_KEY_BUILD_TYPE = 'build_type'
+    CUSTOM_PROP_KEY_SYSTEM_POWER = 'system_power'
+    CUSTOM_PROP_KEY_MODEM_BASEBAND = 'baseband'
+    CUSTOM_PROP_KEY_MODEM_ODPM_POWER= 'modem_odpm_power'
+    CUSTOM_PROP_KEY_DEVICE_NAME = 'device'
+    CUSTOM_PROP_KEY_DEVICE_BUILD_PHASE = 'device_build_phase'
+    CUSTOM_PROP_KEY_MODEM_KIBBLE_POWER = 'modem_kibble_power'
+    CUSTOM_PROP_KEY_TEST_NAME = 'test_name'
+    CUSTOM_PROP_KEY_MODEM_KIBBLE_WO_PCIE_POWER = 'modem_kibble_power_wo_pcie'
+    CUSTOM_PROP_KEY_MODEM_KIBBLE_PCIE_POWER = 'modem_kibble_pcie_power'
+    CUSTOM_PROP_KEY_RFFE_POWER = 'rffe_power'
+    CUSTOM_PROP_KEY_MMWAVE_POWER = 'mmwave_power'
+    # kibble report
+    KIBBLE_SYSTEM_RECORD_NAME = '- name: default_device.C10_EVT_1_1.Monsoon:mA'
+    MODEM_PCIE_RAIL_NAME_LIST = [
+        'PP1800_L2C_PCIEG3',
+        'PP1200_L9C_PCIE',
+        'PP0850_L8C_PCIE'
+    ]
+
+    MODEM_RFFE_RAIL_NAME_LIST = [
+        'PP1200_L31C_RFFE',
+        'VSYS_PWR_RFFE',
+        'PP2800_L33C_RFFE'
+    ]
+
+    MODEM_POWER_RAIL_NAME = 'VSYS_PWR_MODEM'
+
+    MODEM_MMWAVE_RAIL_NAME = 'VSYS_PWR_MMWAVE'
+
+    MONSOON_RAIL_NAME = 'Monsoon'
+
+    # params key
+    MONSOON_VOLTAGE_KEY = 'mon_voltage'
+
+    MDSTEST_APP_APK_NAME = 'mdstest.apk'
+    ADB_CMD_INSTALL = 'install {apk_path}'
+    ADB_CMD_DISABLE_TXAS = 'am instrument -w -e request at+googtxas=2 -e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"'
+    ADB_CMD_SET_NV = ('am instrument -w '
+                      '-e request at+googsetnv=\"{nv_name}\",{nv_index},\"{nv_value}\" '
+                      '-e response wait "com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation"')
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.retryable_exceptions = signals.TestFailure
+        self.power_rails = {}
+        self.pcie_power = 0
+        self.rffe_power = 0
+        self.mmwave_power = 0
+        self.modem_power = 0
+        self.monsoon_power = 0
+
+    def setup_class(self):
+        super().setup_class()
+
+        # preset callbox
+        is_fr2 = 'Fr2' in self.TAG
+        self.cellular_simulator.switch_HCCU_settings(is_fr2=is_fr2)
+
+        self.at_util = AtUtil(self.cellular_dut.ad, self.log)
+
+        # preset UE.
+        self.log.info('Installing mdstest app.')
+        self.install_apk()
+
+        self.log.info('Disable antenna switch.')
+        is_txas_disabled = self.at_util.disable_txas()
+        self.log.info('Disable txas: ' + str(is_txas_disabled))
+
+        # get sim type
+        self.unpack_userparams(has_3gpp_sim=True)
+
+    def setup_test(self):
+        self.cellular_simulator.set_sim_type(self.has_3gpp_sim)
+        try:
+            if 'LTE' in self.test_name:
+                self.at_util.lock_LTE()
+            super().setup_test()
+        except BrokenPipeError:
+            self.log.info('TA crashed test need retry.')
+            self.need_retry = True
+            self.cellular_simulator.recovery_ta()
+            self.cellular_simulator.socket_connect()
+            raise signals.TestFailure('TA crashed mid test, retry needed.')
+        # except:
+        #     # self.log.info('Waiting for device to on.')
+        #     # self.dut.adb.wait_for_device()
+        #     # self.cellular_dut = AndroidCellularDut.AndroidCellularDut(
+        #     # self.android_devices[0], self.log)
+        #     # self.dut.root_adb()
+        #     # # Restart SL4A
+        #     # self.dut.start_services()
+        #     # self.need_retry = True
+        #     raise signals.TestError('Device reboot mid test, retry needed.')
+
+    def install_apk(self):
+        sleep_time = 3
+        for file in self.custom_files:
+            if self.MDSTEST_APP_APK_NAME in file:
+                if not self.cellular_dut.ad.is_apk_installed("com.google.mdstest"):
+                    self.cellular_dut.ad.adb.install("-r -g %s" % file, timeout=300, ignore_status=True)
+        time.sleep(sleep_time)
+        if self.cellular_dut.ad.is_apk_installed("com.google.mdstest"):
+            self.log.info('mdstest installed.')
+        else:
+            self.log.warning('fail to install mdstest.')
+
+    def set_nv(self, nv_name, index, value):
+        cmd = self.ADB_CMD_SET_NV.format(
+            nv_name=nv_name,
+            nv_index=index,
+            nv_value=value
+        )
+        response = str(self.cellular_dut.ad.adb.shell(cmd))
+        self.log.info(response)
+
+    def enable_ims_nr(self):
+        # set !NRCAPA.Gen.VoiceOverNr
+        self.set_nv(
+            nv_name = '!NRCAPA.Gen.VoiceOverNr',
+            index = '0',
+            value = '01'
+        )
+        # set PSS.AIMS.Enable.NRSACONTROL
+        self.set_nv(
+            nv_name = 'PSS.AIMS.Enable.NRSACONTROL',
+            index = '0',
+            value = '00'
+        )
+        # set DS.PSS.AIMS.Enable.NRSACONTROL
+        self.set_nv(
+            nv_name = 'DS.PSS.AIMS.Enable.NRSACONTROL',
+            index = '0',
+            value = '00'
+        )
+        if self.cellular_dut.ad.model == 'oriole':
+            # For P21, NR.CONFIG.MODE/DS.NR.CONFIG.MODE
+            self.set_nv(
+                nv_name = 'NR.CONFIG.MODE',
+                index = '0',
+                value = '11'
+            )
+            # set DS.NR.CONFIG.MODE
+            self.set_nv(
+                nv_name = 'DS.NR.CONFIG.MODE',
+                index = '0',
+                value = '11'
+            )
+        else:
+            # For P22, NASU.NR.CONFIG.MODE to 11
+            self.set_nv(
+                nv_name = 'NASU.NR.CONFIG.MODE',
+                index = '0',
+                value = '11'
+            )
+
+    def get_odpm_values(self):
+        """Get power measure from ODPM.
+
+        Parsing energy table in ODPM file
+        and convert to.
+        Returns:
+            odpm_power_results: a dictionary
+                has key as channel name,
+                and value as power measurement of that channel.
+        """
+        self.log.info('Start calculating power by channel from ODPM report.')
+        odpm_power_results = {}
+
+        # device before P21 don't have ODPM reading
+        if not self.odpm_folder:
+            return odpm_power_results
+
+        # getting ODPM modem power value
+        odpm_file_name = '{}.{}.dumpsys_odpm_{}.txt'.format(
+            self.__class__.__name__,
+            self.current_test_name,
+            'after')
+        odpm_file_path = os.path.join(self.odpm_folder, odpm_file_name)
+        if os.path.exists(odpm_file_path):
+            elapsed_time = None
+            with open(odpm_file_path, 'r') as f:
+                # find energy table in ODPM report
+                for line in f:
+                    if self.ODPM_ENERGY_TABLE_NAME in line:
+                        break
+
+                # get elapse time 2 adb ODPM cmd (mS)
+                elapsed_time_str = f.readline()
+                elapsed_time = float(elapsed_time_str
+                                        .split(':')[1]
+                                        .strip()
+                                        .split(' ')[0])
+                self.log.info(elapsed_time_str)
+
+                # skip column name row
+                next(f)
+
+                # get power of different channel from odpm report
+                for line in f:
+                    if 'End' in line:
+                        break
+                    else:
+                        # parse columns
+                        # example result of line.strip().split()
+                        # ['[VSYS_PWR_DISPLAY]:Display', '1039108.42', 'mWs', '(', '344.69)']
+                        channel, _, _, _, delta_str = line.strip().split()
+                        delta = float(delta_str[:-2].strip())
+
+                        # calculate OPDM power
+                        # delta is a different in cumulative energy
+                        # between 2 adb ODPM cmd
+                        elapsed_time_s = elapsed_time / 1000
+                        power = delta / elapsed_time_s
+                        odpm_power_results[channel] = power
+                        self.log.info(
+                            channel + ' ' + str(power) + ' mW'
+                        )
+        return odpm_power_results
+
+    def _is_any_substring(self, longer_word: str, word_list: List[str]) -> bool:
+        """Check if any word in word list a substring of a longer word."""
+        return any(w in longer_word for w in word_list)
+
+    def parse_power_rails_csv(self):
+        kibble_dir = os.path.join(self.root_output_path, 'Kibble')
+        kibble_csv_path = None
+        if os.path.exists(kibble_dir):
+            for f in os.listdir(kibble_dir):
+                if self.test_name in f and '.csv' in f:
+                    kibble_csv_path = os.path.join(kibble_dir, f)
+                    self.log.info('Kibble csv file path: ' + kibble_csv_path)
+                    break
+
+        self.log.info('Parsing power rails from csv.')
+        if kibble_csv_path:
+            with open(kibble_csv_path, 'r') as f:
+                for line in f:
+                    # railname,val,mA,val,mV,val,mW
+                    railname, _, _, _, _, power, _ = line.split(',')
+                    # parse pcie power
+                    if self._is_any_substring(railname, self.MODEM_PCIE_RAIL_NAME_LIST):
+                        self.log.info(railname + ': ' + power)
+                        self.pcie_power += float(power)
+                    elif self.MODEM_POWER_RAIL_NAME in railname:
+                        self.log.info(railname + ': ' + power)
+                        self.modem_power = float(power)
+                    elif self._is_any_substring(railname, self.MODEM_RFFE_RAIL_NAME_LIST):
+                        self.log.info(railname + ': ' + power)
+                        self.rffe_power = float(power)
+                    elif self.MODEM_MMWAVE_RAIL_NAME in railname:
+                        self.log.info(railname + ': ' + power)
+                        self.mmwave_power = float(power)
+                    elif self.MONSOON_RAIL_NAME == railname:
+                        self.log.info(railname + ': ' + power)
+                        self.monsoon_power = float(power)
+        if self.modem_power:
+            self.power_results[self.test_name] = self.modem_power
+
+    def sponge_upload(self):
+        """Upload result to sponge as custom field."""
+        # test name
+        test_name_arr = self.current_test_name.split('_')
+        test_name_for_sponge = ''.join(
+            word[0].upper() + word[1:].lower()
+                for word in test_name_arr
+                    if word not in ('preset', 'test')
+        )
+
+        # build info
+        build_info = self.cellular_dut.ad.build_info
+        build_id = build_info.get('build_id', 'Unknown')
+        incr_build_id = build_info.get('incremental_build_id', 'Unknown')
+        modem_base_band = self.cellular_dut.ad.adb.getprop(
+            'gsm.version.baseband')
+        build_type = build_info.get('build_type', 'Unknown')
+
+        # device info
+        device_info = self.cellular_dut.ad.device_info
+        device_name = device_info.get('model', 'Unknown')
+        device_build_phase = self.cellular_dut.ad.adb.getprop(
+            'ro.boot.hardware.revision'
+        )
+
+        # power measurement results
+        odpm_power_results = self.get_odpm_values()
+        odpm_power = odpm_power_results.get(self.ODPM_MODEM_CHANNEL_NAME, 0)
+        system_power = 0
+
+        # if kibbles are using, get power from kibble
+        modem_kibble_power_wo_pcie = 0
+        if hasattr(self, 'bitses'):
+            self.parse_power_rails_csv()
+            modem_kibble_power_wo_pcie = self.modem_power - self.pcie_power
+            system_power = self.monsoon_power
+        else:
+            system_power = self.power_results.get(self.test_name, 0)
+
+        self.record_data({
+            'Test Name': self.test_name,
+            'sponge_properties': {
+                self.CUSTOM_PROP_KEY_SYSTEM_POWER: system_power,
+                self.CUSTOM_PROP_KEY_BUILD_ID: build_id,
+                self.CUSTOM_PROP_KEY_INCR_BUILD_ID: incr_build_id,
+                self.CUSTOM_PROP_KEY_MODEM_BASEBAND: modem_base_band,
+                self.CUSTOM_PROP_KEY_BUILD_TYPE: build_type,
+                self.CUSTOM_PROP_KEY_MODEM_ODPM_POWER: odpm_power,
+                self.CUSTOM_PROP_KEY_DEVICE_NAME: device_name,
+                self.CUSTOM_PROP_KEY_DEVICE_BUILD_PHASE: device_build_phase,
+                self.CUSTOM_PROP_KEY_MODEM_KIBBLE_POWER: self.modem_power,
+                self.CUSTOM_PROP_KEY_TEST_NAME: test_name_for_sponge,
+                self.CUSTOM_PROP_KEY_MODEM_KIBBLE_WO_PCIE_POWER: modem_kibble_power_wo_pcie,
+                self.CUSTOM_PROP_KEY_MODEM_KIBBLE_PCIE_POWER: self.pcie_power,
+                self.CUSTOM_PROP_KEY_RFFE_POWER: self.rffe_power,
+                self.CUSTOM_PROP_KEY_MMWAVE_POWER: self.mmwave_power
+            },
+        })
+
+    def teardown_test(self):
+        super().teardown_test()
+        # restore device to ready state for next test
+        self.log.info('Enable mobile data.')
+        self.dut.adb.shell('svc data enable')
+        self.cellular_simulator.detach()
+        self.cellular_dut.toggle_airplane_mode(True)
+
+        # processing result
+        self.sponge_upload()
+        if 'LTE' in self.test_name:
+            self.at_util.clear_lock_band()
\ No newline at end of file
diff --git a/acts_tests/acts_contrib/test_utils/power/cellular/ims_api_connector_utils.py b/acts_tests/acts_contrib/test_utils/power/cellular/ims_api_connector_utils.py
new file mode 100644
index 0000000..cfdc67a
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/power/cellular/ims_api_connector_utils.py
@@ -0,0 +1,228 @@
+# TODO(hmtuan): add type annotation.
+import requests
+import time
+
+class ImsApiConnector():
+    """A wrapper class for Keysight Ims API Connector.
+
+    Keysight provided an API connector application
+    which is a HTTP server running on the same host
+    as Keysight IMS server simulator and client simulator.
+    It allows IMS simulator/app to be controlled via HTTP request.
+
+    Attributes:
+        api_connector_ip: ip of http server.
+        api_connector_port: port of http server.
+        ims_app: type of ims app (client/server).
+        api_token: an arbitrary and unique token-string
+            to identify the link between API connector
+            and ims app.
+        log: logger object.
+    """
+
+    def __init__(self, api_connector_ip,
+                 api_connector_port, ims_app,
+                 api_token, ims_app_ip,
+                 ims_app_port, log):
+        # api connector info
+        self.api_connector_ip = api_connector_ip
+        self.api_connector_port = api_connector_port
+
+        # ims app info
+        self.ims_app = ims_app
+        self.api_token = api_token
+        self.ims_app_ip = ims_app_ip
+        self.ims_app_port = ims_app_port
+
+        self.log = log
+        # construct base url
+        self.base_url = 'http://{addr}:{port}/ims/api/{app}s/{api_token}'.format(
+            addr = self.api_connector_ip,
+            port = self.api_connector_port,
+            app = self.ims_app,
+            api_token = self.api_token
+        )
+
+    def get_base_url(self):
+        return self.base_url
+
+    def create_ims_app_link(self):
+        """Create link between Keysight API Connector to ims app."""
+        self.log.info('Create ims app link: token:ip:port')
+        self.log.info('Creating ims_{app} link: {token}:{target_ip}:{target_port}'.format(
+            app = self.ims_app,
+            token = self.api_token,
+            target_ip = self.ims_app_ip,
+            target_port= self.ims_app_port)
+        )
+
+        request_data = {
+            "targetIpAddress": self.ims_app_ip,
+            "targetWcfPort": self.ims_app_port
+        }
+        self.log.debug(f'Payload to create ims app link: {request_data}')
+        r = requests.post(url = self.get_base_url(), json = request_data)
+
+        self.log.info('HTTP request sent:')
+        self.log.info('-> method: ' + str(r.request.method))
+        self.log.info('-> url: ' + str(r.url))
+        self.log.info('-> status_code: ' + str(r.status_code))
+
+        return (r.status_code == 201)
+
+    def remove_ims_app_link(self):
+        """Remove link between Keysight API Connector to ims app."""
+        self.log.info('Remove ims_{app} link: {token}'.format(
+            app = self.ims_app,
+            token = self.api_token)
+        )
+
+        r = requests.delete(url = self.get_base_url())
+
+        self.log.info('-> method: ' + str(r.request.method))
+        self.log.info('-> url: ' + str(r.url))
+        self.log.info('-> status_code: ' + str(r.status_code))
+
+        return (r.status_code == 200)
+
+    def get_ims_app_property(self, property_name):
+        """Get property value of IMS app.
+
+        Attributes:
+            property_name: name of property to get value.
+        """
+        self.log.info('Getting ims app property: ' + property_name)
+
+        request_url = self.get_base_url() + '/get_property'
+        request_params = {"propertyName": property_name}
+        r = requests.get(url = request_url, params = request_params)
+
+        self.log.info('-> method: ' + str(r.request.method))
+        self.log.info('-> url: ' + str(r.url))
+        self.log.info('-> status_code: ' + str(r.status_code))
+
+        try:
+            res_json = r.json()
+        except:
+            res_json = {'propertyValue': None }
+        prop_value = res_json['propertyValue']
+
+        return prop_value
+
+    def set_ims_app_property(self, property_name, property_value):
+        """Set property value of IMS app.
+
+        Attributes:
+            property_name: name of property to set value.
+            property_value: value to be set.
+        """
+        self.log.info('Setting ims property: ' + property_name + ' = ' + str(property_value))
+
+        request_url = self.get_base_url() + '/set_property'
+        data = {
+            'propertyName': property_name,
+            'propertyValue': property_value
+        }
+        r = requests.post(url = request_url, json = data)
+
+        self.log.info('-> method: ' + str(r.request.method))
+        self.log.info('-> url: ' + str(r.url))
+        self.log.info('-> status_code: ' + str(r.status_code))
+
+        return (r.status_code == 200)
+
+    def ims_api_call_method(self, method_name, method_args=None):
+        """
+        Attributes:
+            method_name: a name of method from Keysight API in string.
+            method_args: a python-array contains
+                arguments for the called API method.
+        Returns:
+            a tuple of (STATUS_BOOL, FUNC_RET_VAL),
+            if STATUS_BOOL is false, FUNC_RET_VAL is questionable/undefined,
+            if STATUS_BOOL is true, FUNC_RET_VAL will be the API function return value
+            or FUNC_RET_VAL is None if the called method return nothing.
+        """
+        self.log.info('Calling ims method: ' + method_name)
+
+        if (method_args == None):
+            method_args = []
+        elif (type(method_args) != list):
+            method_args = [method_args]
+        data = {
+            'methodName': method_name,
+            'arguments': method_args
+        }
+        request_url = self.get_base_url() + '/call_method'
+        r = requests.post(url = request_url, json = data)
+
+        ret_val = None
+
+        if ( ('Content-Type' in r.headers.keys()) and r.headers['Content-Type'] == 'application/json'):
+            # TODO(hmtuan): try json.loads() instead
+            response_body = r.json()
+            if ((response_body != None) and ('returnValue' in response_body.keys())) :
+                ret_val = response_body['returnValue']
+
+        self.log.info('-> method: ' + str(r.request.method))
+        self.log.info('-> url: ' + str(r.url))
+        self.log.info('-> status_code: ' + str(r.status_code))
+        self.log.info('-> ret_val: ' + str(ret_val))
+
+        return (r.status_code == 200), ret_val
+
+    def _is_line_idle(self, call_line_number):
+        is_line_idle_prop = self.get_ims_app_property(
+            f'IVoip.CallLineParams({call_line_number}).SessionState')
+        return is_line_idle_prop == 'Idle'
+
+    def _is_ims_client_app_registered(self):
+        is_registered_prop = self.get_ims_app_property('IComponentControl.IsRegistered')
+        return is_registered_prop == 'True'
+
+    def initiate_call(self, callee_number, call_line_idx=0):
+        """Dial to callee_number.
+
+        Attributes:
+            callee_number: number to be dialed to.
+        """
+        # create IMS-Client API link
+        ret_val = self.create_ims_app_link()
+
+        if not ret_val:
+            raise RuntimeError('Fail to create link to IMS app.')
+
+        # check if IMS-Client is registered, and if not, request client to perform Registration
+        self.log.info('Ensuring client registered.')
+        is_registered = self._is_ims_client_app_registered()
+        if not is_registered:
+            self.log.info('Client not currently registered - registering.')
+            self.ims_api_call_method('ISipConnection.Register()')
+
+        is_registered = self._is_ims_client_app_registered()
+        if not is_registered:
+            raise RuntimeError('Failed to register IMS-client to IMS-server.')
+
+        # switch to call-line #1 (idx = 0)
+        self.log.info('Switching to call-line #1.')
+        self.set_ims_app_property('IVoip.SelectedCallLine', call_line_idx)
+
+        # check whether the call-line #1 is ready for dialling
+        is_line1_idle = self._is_line_idle(call_line_idx)
+        if not is_line1_idle:
+            raise RuntimeError('Call-line not is not in indle state.')
+
+        # entering callee number for call-line #1
+        self.log.info(f'Enter callee number: {callee_number}.')
+        self.set_ims_app_property('IVoip.CallLineParams(0).CallLocation', callee_number)
+
+        # dial entered callee number
+        self.log.info('Dialling call.')
+        self.ims_api_call_method('IVoip.Dial()')
+
+        time.sleep(5)
+
+        # check if dial success (not idle)
+        is_line1_idle = self._is_line_idle(call_line_idx)
+        if is_line1_idle:
+            raise RuntimeError('Fail to dial.')
diff --git a/acts_tests/acts_contrib/test_utils/power/loggers/protos/power_metric_pb2.py b/acts_tests/acts_contrib/test_utils/power/loggers/protos/power_metric_pb2.py
index d1682cc..32f472a 100644
--- a/acts_tests/acts_contrib/test_utils/power/loggers/protos/power_metric_pb2.py
+++ b/acts_tests/acts_contrib/test_utils/power/loggers/protos/power_metric_pb2.py
@@ -2,9 +2,9 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: power_metric.proto
 """Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -13,204 +13,15 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='power_metric.proto',
-  package='wireless.android.platform.testing.power.metrics',
-  syntax='proto2',
-  serialized_options=None,
-  create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x12power_metric.proto\x12/wireless.android.platform.testing.power.metrics\"\x80\x03\n\x0bPowerMetric\x12\x11\n\tavg_power\x18\x01 \x01(\x02\x12\x0f\n\x07testbed\x18\x02 \x01(\t\x12]\n\x0f\x63\x65llular_metric\x18\x03 \x01(\x0b\x32\x44.wireless.android.platform.testing.power.metrics.PowerCellularMetric\x12\x0e\n\x06\x62ranch\x18\x04 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x05 \x01(\t\x12\x0e\n\x06target\x18\x06 \x01(\t\x12\x13\n\x0b\x61vg_current\x18\x07 \x01(\x02\x12\x0f\n\x07voltage\x18\x08 \x01(\x02\x12\x1f\n\x17test_suite_display_name\x18\t \x01(\t\x12\x1e\n\x16test_case_display_name\x18\n \x01(\t\x12\x1c\n\x14incremental_build_id\x18\x0b \x01(\t\x12\x1d\n\x15\x61vg_current_threshold\x18\x0c \x01(\x02\x12\x18\n\x10pass_fail_status\x18\r \x01(\t\"}\n\x13PowerCellularMetric\x12\x13\n\x0b\x61vg_dl_tput\x18\x01 \x01(\x02\x12\x13\n\x0b\x61vg_ul_tput\x18\x02 \x01(\x02\x12\x1d\n\x15\x61vg_dl_tput_threshold\x18\x03 \x01(\x02\x12\x1d\n\x15\x61vg_ul_tput_threshold\x18\x04 \x01(\x02'
-)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12power_metric.proto\x12/wireless.android.platform.testing.power.metrics\"\x80\x03\n\x0bPowerMetric\x12\x11\n\tavg_power\x18\x01 \x01(\x02\x12\x0f\n\x07testbed\x18\x02 \x01(\t\x12]\n\x0f\x63\x65llular_metric\x18\x03 \x01(\x0b\x32\x44.wireless.android.platform.testing.power.metrics.PowerCellularMetric\x12\x0e\n\x06\x62ranch\x18\x04 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x05 \x01(\t\x12\x0e\n\x06target\x18\x06 \x01(\t\x12\x13\n\x0b\x61vg_current\x18\x07 \x01(\x02\x12\x0f\n\x07voltage\x18\x08 \x01(\x02\x12\x1f\n\x17test_suite_display_name\x18\t \x01(\t\x12\x1e\n\x16test_case_display_name\x18\n \x01(\t\x12\x1c\n\x14incremental_build_id\x18\x0b \x01(\t\x12\x1d\n\x15\x61vg_current_threshold\x18\x0c \x01(\x02\x12\x18\n\x10pass_fail_status\x18\r \x01(\t\"}\n\x13PowerCellularMetric\x12\x13\n\x0b\x61vg_dl_tput\x18\x01 \x01(\x02\x12\x13\n\x0b\x61vg_ul_tput\x18\x02 \x01(\x02\x12\x1d\n\x15\x61vg_dl_tput_threshold\x18\x03 \x01(\x02\x12\x1d\n\x15\x61vg_ul_tput_threshold\x18\x04 \x01(\x02')
 
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'power_metric_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-
-
-_POWERMETRIC = _descriptor.Descriptor(
-  name='PowerMetric',
-  full_name='wireless.android.platform.testing.power.metrics.PowerMetric',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='avg_power', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.avg_power', index=0,
-      number=1, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='testbed', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.testbed', index=1,
-      number=2, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='cellular_metric', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.cellular_metric', index=2,
-      number=3, type=11, cpp_type=10, label=1,
-      has_default_value=False, default_value=None,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='branch', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.branch', index=3,
-      number=4, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='build_id', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.build_id', index=4,
-      number=5, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='target', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.target', index=5,
-      number=6, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='avg_current', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.avg_current', index=6,
-      number=7, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='voltage', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.voltage', index=7,
-      number=8, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='test_suite_display_name', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.test_suite_display_name', index=8,
-      number=9, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='test_case_display_name', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.test_case_display_name', index=9,
-      number=10, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='incremental_build_id', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.incremental_build_id', index=10,
-      number=11, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='avg_current_threshold', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.avg_current_threshold', index=11,
-      number=12, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='pass_fail_status', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.pass_fail_status', index=12,
-      number=13, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=72,
-  serialized_end=456,
-)
-
-
-_POWERCELLULARMETRIC = _descriptor.Descriptor(
-  name='PowerCellularMetric',
-  full_name='wireless.android.platform.testing.power.metrics.PowerCellularMetric',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='avg_dl_tput', full_name='wireless.android.platform.testing.power.metrics.PowerCellularMetric.avg_dl_tput', index=0,
-      number=1, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='avg_ul_tput', full_name='wireless.android.platform.testing.power.metrics.PowerCellularMetric.avg_ul_tput', index=1,
-      number=2, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='avg_dl_tput_threshold', full_name='wireless.android.platform.testing.power.metrics.PowerCellularMetric.avg_dl_tput_threshold', index=2,
-      number=3, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='avg_ul_tput_threshold', full_name='wireless.android.platform.testing.power.metrics.PowerCellularMetric.avg_ul_tput_threshold', index=3,
-      number=4, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=458,
-  serialized_end=583,
-)
-
-_POWERMETRIC.fields_by_name['cellular_metric'].message_type = _POWERCELLULARMETRIC
-DESCRIPTOR.message_types_by_name['PowerMetric'] = _POWERMETRIC
-DESCRIPTOR.message_types_by_name['PowerCellularMetric'] = _POWERCELLULARMETRIC
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-PowerMetric = _reflection.GeneratedProtocolMessageType('PowerMetric', (_message.Message,), {
-  'DESCRIPTOR' : _POWERMETRIC,
-  '__module__' : 'power_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.power.metrics.PowerMetric)
-  })
-_sym_db.RegisterMessage(PowerMetric)
-
-PowerCellularMetric = _reflection.GeneratedProtocolMessageType('PowerCellularMetric', (_message.Message,), {
-  'DESCRIPTOR' : _POWERCELLULARMETRIC,
-  '__module__' : 'power_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.power.metrics.PowerCellularMetric)
-  })
-_sym_db.RegisterMessage(PowerCellularMetric)
-
-
+  DESCRIPTOR._options = None
+  _POWERMETRIC._serialized_start=72
+  _POWERMETRIC._serialized_end=456
+  _POWERCELLULARMETRIC._serialized_start=458
+  _POWERCELLULARMETRIC._serialized_end=583
 # @@protoc_insertion_point(module_scope)
diff --git a/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py b/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py
index c7cb108..b3b0e3b 100644
--- a/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/tel/TelephonyBaseTest.py
@@ -24,69 +24,76 @@
 import time
 
 from acts import asserts
+from acts import records
 from acts import signals
+from acts import utils
 from acts.base_test import BaseTestClass
+from acts.controllers.adb_lib.error import AdbCommandError
 from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
 from acts.keys import Config
-from acts import records
-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_bootloader_utils import flash_radio
+from acts_contrib.test_utils.tel.tel_defines import CHIPSET_MODELS_LIST
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_defines import MULTI_SIM_CONFIG, SINGLE_SIM_CONFIG
 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 get_tcpdump_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_dsp_logger_p21
+from acts_contrib.test_utils.tel.tel_logging_utils import start_dsp_logger
 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
-from acts_contrib.test_utils.tel.tel_logging_utils import stop_qxdm_logger
-from acts_contrib.test_utils.tel.tel_logging_utils import start_sdm_loggers
 from acts_contrib.test_utils.tel.tel_logging_utils import start_sdm_logger
-from acts_contrib.test_utils.tel.tel_logging_utils import stop_sdm_logger
+from acts_contrib.test_utils.tel.tel_logging_utils import start_sdm_loggers
 from acts_contrib.test_utils.tel.tel_logging_utils import start_tcpdumps
+from acts_contrib.test_utils.tel.tel_logging_utils import stop_qxdm_logger
+from acts_contrib.test_utils.tel.tel_logging_utils import stop_sdm_logger
 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_phones_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 get_subid_from_slot_index
 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 activate_esim_using_suw
+from acts_contrib.test_utils.tel.tel_test_utils import activate_google_fi_account
+from acts_contrib.test_utils.tel.tel_test_utils import adb_disable_verity
+from acts_contrib.test_utils.tel.tel_test_utils import add_google_account
 from acts_contrib.test_utils.tel.tel_test_utils import build_id_override
+from acts_contrib.test_utils.tel.tel_test_utils import check_google_fi_activated
 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 force_connectivity_metrics_upload
 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 install_googleaccountutil_apk
+from acts_contrib.test_utils.tel.tel_test_utils import install_googlefi_apk
+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 print_radio_info
 from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
 from acts_contrib.test_utils.tel.tel_test_utils import recover_build_id
-from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
 from acts_contrib.test_utils.tel.tel_test_utils import set_phone_screen_on
 from acts_contrib.test_utils.tel.tel_test_utils import set_phone_silent_mode
+from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
 from acts_contrib.test_utils.tel.tel_test_utils import synchronize_device_time
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts_contrib.test_utils.tel.tel_test_utils import unlock_sim
 from acts_contrib.test_utils.tel.tel_test_utils import wait_for_sim_ready_by_adb
 from acts_contrib.test_utils.tel.tel_test_utils import wait_for_sims_ready_by_adb
-from acts_contrib.test_utils.tel.tel_test_utils import install_googleaccountutil_apk
-from acts_contrib.test_utils.tel.tel_test_utils import add_google_account
-from acts_contrib.test_utils.tel.tel_test_utils import install_googlefi_apk
-from acts_contrib.test_utils.tel.tel_test_utils import activate_google_fi_account
-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_wifi_utils import ensure_wifi_connected
 
 
+REMOUNT_REBOOT_MSG = "Now reboot your device for settings to take effect"
+
+
 class TelephonyBaseTest(BaseTestClass):
     # Use for logging in the test cases to facilitate
     # faster log lookup and reduce ambiguity in logging.
@@ -158,6 +165,8 @@
         self.log_path = getattr(logging, "log_path", None)
         self.qxdm_log = self.user_params.get("qxdm_log", True)
         self.sdm_log = self.user_params.get("sdm_log", False)
+        self.tcpdump_log = self.user_params.get("tcpdump_log", True)
+        self.dsp_log = self.user_params.get("dsp_log", False)
         self.dsp_log_p21 = self.user_params.get("dsp_log_p21", False)
         self.enable_radio_log_on = self.user_params.get(
             "enable_radio_log_on", False)
@@ -197,6 +206,8 @@
 
         tasks = [(self._init_device, [ad]) for ad in self.android_devices]
         multithread_func(self.log, tasks)
+        self.reboot_before_test = self.user_params.get(
+            "reboot_before_test", False)
         self.skip_reset_between_cases = self.user_params.get(
             "skip_reset_between_cases", True)
         self.log_path = getattr(logging, "log_path", None)
@@ -258,6 +269,7 @@
     def _setup_device(self, ad, sim_conf_file, qxdm_log_mask_cfg=None):
         ad.qxdm_log = getattr(ad, "qxdm_log", self.qxdm_log)
         ad.sdm_log = getattr(ad, "sdm_log", self.sdm_log)
+        ad.dsp_log = getattr(ad, "dsp_log", self.dsp_log)
         ad.dsp_log_p21 = getattr(ad, "dsp_log_p21", self.dsp_log_p21)
         if self.user_params.get("enable_connectivity_metrics", False):
             enable_connectivity_metrics(ad)
@@ -269,6 +281,7 @@
                 new_build_id=self.user_params.get("build_id_override_with",
                                                   None),
                 postfix=build_postfix)
+
         if self.enable_radio_log_on:
             enable_radio_log_on(ad)
         list_of_models = CHIPSET_MODELS_LIST
@@ -281,8 +294,26 @@
                              % phone_mode)
                 reboot_device(ad)
 
-        if ad.dsp_log_p21:
-            start_dsp_logger_p21(ad)
+        if "_test" not in ad.build_info["build_id"]:
+            ad.ensure_verity_disabled()
+            try:
+                ad.adb.remount()
+            except AdbCommandError as e:
+                if REMOUNT_REBOOT_MSG in e.stderr:
+                    ad.reboot()
+                    ad.adb.remount()
+            build_id = ad.build_info["build_id"].replace(".", r"\.")
+            ad.adb.shell("sed -i '/^ro.build.id=/ "
+                        f"s/{build_id}/&_test/g' /system/build.prop")
+            ad.adb.shell("sed -i '/^ro.build.description=/ "
+                        f"s/{build_id}/&_test/g' /system/build.prop")
+
+        if ad.dsp_log:
+            start_dsp_logger(ad)
+        elif ad.dsp_log_p21:
+            start_dsp_logger(ad, p21=True)
+        else:
+            ad.reboot()
         stop_qxdm_logger(ad)
         if ad.qxdm_log:
             qxdm_log_mask = getattr(ad, "qxdm_log_mask", None)
@@ -335,17 +366,36 @@
             # eSIM needs activation
             activate_esim_using_suw(ad)
             ensure_phone_idle(self.log, ad)
-            setup_droid_properties(self.log, ad, sim_conf_file)
+            if getattr(ad, 'mep', False):
+                setup_droid_properties(self.log, ad, sim_conf_file, True)
+            else:
+                setup_droid_properties(self.log, ad, sim_conf_file)
         elif self.user_params.get("Attenuator"):
             ad.log.info("Device in chamber room")
             ensure_phone_idle(self.log, ad)
-            setup_droid_properties(self.log, ad, sim_conf_file)
+            if getattr(ad, 'mep', False):
+                setup_droid_properties(self.log, ad, sim_conf_file, True)
+            else:
+                setup_droid_properties(self.log, ad, sim_conf_file)
         else:
             self.wait_for_sim_ready(ad)
             ensure_phone_default_state(self.log, ad)
-            setup_droid_properties(self.log, ad, sim_conf_file)
+            if getattr(ad, 'mep', False):
+                setup_droid_properties(self.log, ad, sim_conf_file, True)
+            else:
+                setup_droid_properties(self.log, ad, sim_conf_file)
 
-        if getattr(ad, 'dsds', False):
+        if getattr(ad, 'mep', False):
+            default_slot = getattr(ad, "default_slot", 1)
+            if get_subid_from_slot_index(ad.log, ad, default_slot) != INVALID_SUB_ID:
+                ad.log.info("Slot %s is the default slot.", default_slot)
+                set_default_sub_for_all_services(ad, default_slot)
+            else:
+                ad.log.warning("Slot %s is NOT a valid slot. Slot %s will be used by default.",
+                    default_slot, 1-default_slot)
+                set_default_sub_for_all_services(ad, 1-default_slot)
+                setattr(ad, "default_slot", 1-default_slot)
+        elif getattr(ad, 'dsds', False):
             default_slot = getattr(ad, "default_slot", 0)
             if get_subid_from_slot_index(ad.log, ad, default_slot) != INVALID_SUB_ID:
                 ad.log.info("Slot %s is the default slot.", default_slot)
@@ -480,10 +530,12 @@
             start_qxdm_loggers(self.log, self.android_devices, self.begin_time)
         if getattr(self, "sdm_log", False):
             start_sdm_loggers(self.log, self.android_devices)
-        if getattr(self, "tcpdump_log", False) or "wfc" in self.test_name or (
-            "iwlan" in self.test_name):
+        if getattr(self, "tcpdump_log", True):
             mask = getattr(self, "tcpdump_mask", "all")
-            interface = getattr(self, "tcpdump_interface", "wlan0")
+            if "wfc" in self.test_name or "iwlan" in self.test_name:
+                interface = getattr(self, "tcpdump_interface", "wlan0")
+            else:
+                interface = getattr(self, "tcpdump_interface", "any")
             start_tcpdumps(
                 self.android_devices,
                 begin_time=self.begin_time,
@@ -492,6 +544,8 @@
         else:
             stop_tcpdumps(self.android_devices)
         for ad in self.android_devices:
+            if self.reboot_before_test:
+                ad.reboot()
             if self.skip_reset_between_cases:
                 ensure_phone_idle(self.log, ad)
             else:
@@ -503,6 +557,9 @@
             match = re.search(r"\d+-\d+\s\d+:\d+:\d+.\d+", output)
             if match:
                 ad.test_log_begin_time = match.group(0)
+            if self.user_params.get("apm_before_test", None):
+                toggle_airplane_mode(self.log, ad, True, False)
+                toggle_airplane_mode(self.log, ad, False, False)
 
     def teardown_test(self):
         stop_tcpdumps(self.android_devices)
@@ -514,6 +571,7 @@
             time.sleep(3)
             ad.screenshot(f"{ad.serial}_last_screen")
         self._take_bug_report(test_name, begin_time)
+        ensure_phones_default_state(self.log, self.android_devices)
 
     def on_pass(self, test_name, begin_time):
         if self.save_passing_logs:
diff --git a/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_metric_pb2.py b/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_metric_pb2.py
index e191960..f8a50d8 100644
--- a/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_metric_pb2.py
+++ b/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_metric_pb2.py
@@ -2,9 +2,9 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: telephony_metric.proto
 """Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -13,189 +13,17 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='telephony_metric.proto',
-  package='wireless.android.platform.testing.telephony.metrics',
-  syntax='proto2',
-  serialized_options=None,
-  create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x16telephony_metric.proto\x12\x33wireless.android.platform.testing.telephony.metrics\"\xf6\x04\n\x18TelephonyVoiceTestResult\x12h\n\x06result\x18\x01 \x01(\x0e\x32X.wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult.CallResult\x12\x1f\n\x17\x63\x61ll_setup_time_latency\x18\x02 \x01(\x02\"\xce\x03\n\nCallResult\x12%\n\x18UNAVAILABLE_NETWORK_TYPE\x10\xfe\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x1f\n\x12\x43\x41LL_SETUP_FAILURE\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x0b\n\x07SUCCESS\x10\x00\x12\x13\n\x0fINITIATE_FAILED\x10\x01\x12\"\n\x1eNO_RING_EVENT_OR_ANSWER_FAILED\x10\x02\x12\x14\n\x10NO_CALL_ID_FOUND\x10\x03\x12.\n*CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT\x10\x04\x12/\n+AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT\x10\x05\x12*\n&AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED\x10\x06\x12\x31\n-CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT\x10\x07\x12,\n(CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED\x10\x08\x12\x14\n\x10\x43\x41LL_HANGUP_FAIL\x10\t\x12\x18\n\x14\x43\x41LL_ID_CLEANUP_FAIL\x10\n\"|\n\x1aTelephonyVoiceStressResult\x12^\n\x07results\x18\x01 \x03(\x0b\x32M.wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult'
-)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16telephony_metric.proto\x12\x33wireless.android.platform.testing.telephony.metrics\"\xf6\x04\n\x18TelephonyVoiceTestResult\x12h\n\x06result\x18\x01 \x01(\x0e\x32X.wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult.CallResult\x12\x1f\n\x17\x63\x61ll_setup_time_latency\x18\x02 \x01(\x02\"\xce\x03\n\nCallResult\x12%\n\x18UNAVAILABLE_NETWORK_TYPE\x10\xfe\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x1f\n\x12\x43\x41LL_SETUP_FAILURE\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x0b\n\x07SUCCESS\x10\x00\x12\x13\n\x0fINITIATE_FAILED\x10\x01\x12\"\n\x1eNO_RING_EVENT_OR_ANSWER_FAILED\x10\x02\x12\x14\n\x10NO_CALL_ID_FOUND\x10\x03\x12.\n*CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT\x10\x04\x12/\n+AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT\x10\x05\x12*\n&AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED\x10\x06\x12\x31\n-CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT\x10\x07\x12,\n(CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED\x10\x08\x12\x14\n\x10\x43\x41LL_HANGUP_FAIL\x10\t\x12\x18\n\x14\x43\x41LL_ID_CLEANUP_FAIL\x10\n\"|\n\x1aTelephonyVoiceStressResult\x12^\n\x07results\x18\x01 \x03(\x0b\x32M.wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult')
 
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'telephony_metric_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-
-_TELEPHONYVOICETESTRESULT_CALLRESULT = _descriptor.EnumDescriptor(
-  name='CallResult',
-  full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult.CallResult',
-  filename=None,
-  file=DESCRIPTOR,
-  create_key=_descriptor._internal_create_key,
-  values=[
-    _descriptor.EnumValueDescriptor(
-      name='UNAVAILABLE_NETWORK_TYPE', index=0, number=-2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='CALL_SETUP_FAILURE', index=1, number=-1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='SUCCESS', index=2, number=0,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='INITIATE_FAILED', index=3, number=1,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='NO_RING_EVENT_OR_ANSWER_FAILED', index=4, number=2,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='NO_CALL_ID_FOUND', index=5, number=3,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT', index=6, number=4,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT', index=7, number=5,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED', index=8, number=6,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT', index=9, number=7,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED', index=10, number=8,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='CALL_HANGUP_FAIL', index=11, number=9,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-    _descriptor.EnumValueDescriptor(
-      name='CALL_ID_CLEANUP_FAIL', index=12, number=10,
-      serialized_options=None,
-      type=None,
-      create_key=_descriptor._internal_create_key),
-  ],
-  containing_type=None,
-  serialized_options=None,
-  serialized_start=248,
-  serialized_end=710,
-)
-_sym_db.RegisterEnumDescriptor(_TELEPHONYVOICETESTRESULT_CALLRESULT)
-
-
-_TELEPHONYVOICETESTRESULT = _descriptor.Descriptor(
-  name='TelephonyVoiceTestResult',
-  full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='result', full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult.result', index=0,
-      number=1, type=14, cpp_type=8, label=1,
-      has_default_value=False, default_value=-2,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='call_setup_time_latency', full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult.call_setup_time_latency', index=1,
-      number=2, type=2, cpp_type=6, label=1,
-      has_default_value=False, default_value=float(0),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-    _TELEPHONYVOICETESTRESULT_CALLRESULT,
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=80,
-  serialized_end=710,
-)
-
-
-_TELEPHONYVOICESTRESSRESULT = _descriptor.Descriptor(
-  name='TelephonyVoiceStressResult',
-  full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceStressResult',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='results', full_name='wireless.android.platform.testing.telephony.metrics.TelephonyVoiceStressResult.results', index=0,
-      number=1, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=712,
-  serialized_end=836,
-)
-
-_TELEPHONYVOICETESTRESULT.fields_by_name['result'].enum_type = _TELEPHONYVOICETESTRESULT_CALLRESULT
-_TELEPHONYVOICETESTRESULT_CALLRESULT.containing_type = _TELEPHONYVOICETESTRESULT
-_TELEPHONYVOICESTRESSRESULT.fields_by_name['results'].message_type = _TELEPHONYVOICETESTRESULT
-DESCRIPTOR.message_types_by_name['TelephonyVoiceTestResult'] = _TELEPHONYVOICETESTRESULT
-DESCRIPTOR.message_types_by_name['TelephonyVoiceStressResult'] = _TELEPHONYVOICESTRESSRESULT
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-TelephonyVoiceTestResult = _reflection.GeneratedProtocolMessageType('TelephonyVoiceTestResult', (_message.Message,), {
-  'DESCRIPTOR' : _TELEPHONYVOICETESTRESULT,
-  '__module__' : 'telephony_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.telephony.metrics.TelephonyVoiceTestResult)
-  })
-_sym_db.RegisterMessage(TelephonyVoiceTestResult)
-
-TelephonyVoiceStressResult = _reflection.GeneratedProtocolMessageType('TelephonyVoiceStressResult', (_message.Message,), {
-  'DESCRIPTOR' : _TELEPHONYVOICESTRESSRESULT,
-  '__module__' : 'telephony_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.telephony.metrics.TelephonyVoiceStressResult)
-  })
-_sym_db.RegisterMessage(TelephonyVoiceStressResult)
-
-
+  DESCRIPTOR._options = None
+  _TELEPHONYVOICETESTRESULT._serialized_start=80
+  _TELEPHONYVOICETESTRESULT._serialized_end=710
+  _TELEPHONYVOICETESTRESULT_CALLRESULT._serialized_start=248
+  _TELEPHONYVOICETESTRESULT_CALLRESULT._serialized_end=710
+  _TELEPHONYVOICESTRESSRESULT._serialized_start=712
+  _TELEPHONYVOICESTRESSRESULT._serialized_end=836
 # @@protoc_insertion_point(module_scope)
diff --git a/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_stress_metric_pb2.py b/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_stress_metric_pb2.py
index 6ccec95..37476b0 100644
--- a/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_stress_metric_pb2.py
+++ b/acts_tests/acts_contrib/test_utils/tel/loggers/protos/telephony_stress_metric_pb2.py
@@ -1,10 +1,10 @@
 # -*- coding: utf-8 -*-
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: telephony_stress_metric.proto
-
+"""Generated protocol buffer code."""
+from google.protobuf.internal import builder as _builder
 from google.protobuf import descriptor as _descriptor
-from google.protobuf import message as _message
-from google.protobuf import reflection as _reflection
+from google.protobuf import descriptor_pool as _descriptor_pool
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -13,107 +13,17 @@
 
 
 
-DESCRIPTOR = _descriptor.FileDescriptor(
-  name='telephony_stress_metric.proto',
-  package='wireless.android.platform.testing.telephony.metrics',
-  syntax='proto2',
-  serialized_options=None,
-  create_key=_descriptor._internal_create_key,
-  serialized_pb=b'\n\x1dtelephony_stress_metric.proto\x12\x33wireless.android.platform.testing.telephony.metrics\"\xc6\x01\n\x19TelephonyStressTestResult\x12u\n\x0cresults_dict\x18\x01 \x03(\x0b\x32_.wireless.android.platform.testing.telephony.metrics.TelephonyStressTestResult.ResultsDictEntry\x1a\x32\n\x10ResultsDictEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01'
-)
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1dtelephony_stress_metric.proto\x12\x33wireless.android.platform.testing.telephony.metrics\"\xc6\x01\n\x19TelephonyStressTestResult\x12u\n\x0cresults_dict\x18\x01 \x03(\x0b\x32_.wireless.android.platform.testing.telephony.metrics.TelephonyStressTestResult.ResultsDictEntry\x1a\x32\n\x10ResultsDictEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01')
 
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'telephony_stress_metric_pb2', globals())
+if _descriptor._USE_C_DESCRIPTORS == False:
 
-
-
-_TELEPHONYSTRESSTESTRESULT_RESULTSDICTENTRY = _descriptor.Descriptor(
-  name='ResultsDictEntry',
-  full_name='wireless.android.platform.testing.telephony.metrics.TelephonyStressTestResult.ResultsDictEntry',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='key', full_name='wireless.android.platform.testing.telephony.metrics.TelephonyStressTestResult.ResultsDictEntry.key', index=0,
-      number=1, type=9, cpp_type=9, label=1,
-      has_default_value=False, default_value=b"".decode('utf-8'),
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-    _descriptor.FieldDescriptor(
-      name='value', full_name='wireless.android.platform.testing.telephony.metrics.TelephonyStressTestResult.ResultsDictEntry.value', index=1,
-      number=2, type=5, cpp_type=1, label=1,
-      has_default_value=False, default_value=0,
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[],
-  enum_types=[
-  ],
-  serialized_options=b'8\001',
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=235,
-  serialized_end=285,
-)
-
-_TELEPHONYSTRESSTESTRESULT = _descriptor.Descriptor(
-  name='TelephonyStressTestResult',
-  full_name='wireless.android.platform.testing.telephony.metrics.TelephonyStressTestResult',
-  filename=None,
-  file=DESCRIPTOR,
-  containing_type=None,
-  create_key=_descriptor._internal_create_key,
-  fields=[
-    _descriptor.FieldDescriptor(
-      name='results_dict', full_name='wireless.android.platform.testing.telephony.metrics.TelephonyStressTestResult.results_dict', index=0,
-      number=1, type=11, cpp_type=10, label=3,
-      has_default_value=False, default_value=[],
-      message_type=None, enum_type=None, containing_type=None,
-      is_extension=False, extension_scope=None,
-      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
-  ],
-  extensions=[
-  ],
-  nested_types=[_TELEPHONYSTRESSTESTRESULT_RESULTSDICTENTRY, ],
-  enum_types=[
-  ],
-  serialized_options=None,
-  is_extendable=False,
-  syntax='proto2',
-  extension_ranges=[],
-  oneofs=[
-  ],
-  serialized_start=87,
-  serialized_end=285,
-)
-
-_TELEPHONYSTRESSTESTRESULT_RESULTSDICTENTRY.containing_type = _TELEPHONYSTRESSTESTRESULT
-_TELEPHONYSTRESSTESTRESULT.fields_by_name['results_dict'].message_type = _TELEPHONYSTRESSTESTRESULT_RESULTSDICTENTRY
-DESCRIPTOR.message_types_by_name['TelephonyStressTestResult'] = _TELEPHONYSTRESSTESTRESULT
-_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-
-TelephonyStressTestResult = _reflection.GeneratedProtocolMessageType('TelephonyStressTestResult', (_message.Message,), {
-
-  'ResultsDictEntry' : _reflection.GeneratedProtocolMessageType('ResultsDictEntry', (_message.Message,), {
-    'DESCRIPTOR' : _TELEPHONYSTRESSTESTRESULT_RESULTSDICTENTRY,
-    '__module__' : 'telephony_stress_metric_pb2'
-    # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.telephony.metrics.TelephonyStressTestResult.ResultsDictEntry)
-    })
-  ,
-  'DESCRIPTOR' : _TELEPHONYSTRESSTESTRESULT,
-  '__module__' : 'telephony_stress_metric_pb2'
-  # @@protoc_insertion_point(class_scope:wireless.android.platform.testing.telephony.metrics.TelephonyStressTestResult)
-  })
-_sym_db.RegisterMessage(TelephonyStressTestResult)
-_sym_db.RegisterMessage(TelephonyStressTestResult.ResultsDictEntry)
-
-
-_TELEPHONYSTRESSTESTRESULT_RESULTSDICTENTRY._options = None
+  DESCRIPTOR._options = None
+  _TELEPHONYSTRESSTESTRESULT_RESULTSDICTENTRY._options = None
+  _TELEPHONYSTRESSTESTRESULT_RESULTSDICTENTRY._serialized_options = b'8\001'
+  _TELEPHONYSTRESSTESTRESULT._serialized_start=87
+  _TELEPHONYSTRESSTESTRESULT._serialized_end=285
+  _TELEPHONYSTRESSTESTRESULT_RESULTSDICTENTRY._serialized_start=235
+  _TELEPHONYSTRESSTESTRESULT_RESULTSDICTENTRY._serialized_end=285
 # @@protoc_insertion_point(module_scope)
diff --git a/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_metric_logger.py b/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_metric_logger.py
index 9869390..2f4304c 100644
--- a/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_metric_logger.py
+++ b/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_metric_logger.py
@@ -14,17 +14,14 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-import base64
 import os
-import time
 
 from acts.metrics.core import ProtoMetric
 from acts.metrics.logger import MetricLogger
 from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
 
 # Initializes the path to the protobuf
-PROTO_PATH = os.path.join(os.path.dirname(__file__),
-                          'protos',
+PROTO_PATH = os.path.join(os.path.dirname(__file__), 'protos',
                           'telephony_metric.proto')
 
 
@@ -46,4 +43,3 @@
         metric = ProtoMetric(name='telephony_voice_test_result',
                              data=self.proto)
         return self.publisher.publish(metric)
-
diff --git a/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_stress_metric_logger.py b/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_stress_metric_logger.py
index 456e8b2..a352dfc 100644
--- a/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_stress_metric_logger.py
+++ b/acts_tests/acts_contrib/test_utils/tel/loggers/telephony_stress_metric_logger.py
@@ -14,17 +14,14 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-import base64
 import os
-import time
 
 from acts.metrics.core import ProtoMetric
 from acts.metrics.logger import MetricLogger
 from acts_contrib.test_utils.tel.loggers.protos.telephony_stress_metric_pb2 import TelephonyStressTestResult
 
 # Initializes the path to the protobuf
-PROTO_PATH = os.path.join(os.path.dirname(__file__),
-                          'protos',
+PROTO_PATH = os.path.join(os.path.dirname(__file__), 'protos',
                           'telephony_stress_metric.proto')
 
 
@@ -46,4 +43,3 @@
         metric = ProtoMetric(name='telephony_stress_test_result',
                              data=self.proto)
         return self.publisher.publish(metric)
-
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 0b6856e..16ce7b7 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
@@ -2894,7 +2894,7 @@
     try:
         while (time_var < wait_time):
             time_var += 30
-            recovery = ad.search_logcat("doRecovery() cleanup all connections",
+            recovery = ad.search_logcat("doRecovery().*cleanup all connections",
                                          begin_time)
             if recovery:
                 ad.log.info("Recovery Performed here - %s",
@@ -2904,4 +2904,4 @@
             time.sleep(30)
     except Exception as e:
         ad.log.error(e)
-    return data_stall_recovery
\ No newline at end of file
+    return data_stall_recovery
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 042bd7a..08d3fc6 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_defines.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_defines.py
@@ -14,6 +14,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import enum
+
 ###############################################
 # TIMERS
 ###############################################
@@ -259,6 +261,9 @@
 # invalid Subscription ID
 INVALID_SUB_ID = -1
 
+# invalid port index
+INVALID_PORT_INDEX = -1
+
 # invalid SIM slot index
 INVALID_SIM_SLOT_INDEX = -1
 
@@ -701,6 +706,7 @@
 CARRIER_TEST_CONF_XML_PATH = "/data/user_de/0/com.android.phone/files/"
 MAIN_ACTIVITY = "android.intent.action.MAIN"
 CBR_PACKAGE = "com.google.android.cellbroadcastreceiver"
+CBR_APEX_PACKAGE = "com.google.android.cellbroadcast"
 SYSUI_PACKAGE = "com.android.systemui"
 CBR_ACTIVITY = "com.android.cellbroadcastreceiver.CellBroadcastSettings"
 CBR_TEST_APK = "com.android.cellbroadcastreceiver.tests"
@@ -710,9 +716,14 @@
 WAIT_TIME_FOR_ALERTS_TO_POPULATE = 60
 WAIT_TIME_FOR_UI = 5
 SCROLL_DOWN = "input swipe 300 900 300 300"
+SLOW_SCROLL_DOWN = "input swipe 300 900 300 100"
+SCROLL_UP = "input swipe 300 500 300 900"
+KEYEVENT_DEL = 'input keyevent KEYCODE_DEL'
 WAIT_TIME_FOR_ALERT_TO_RECEIVE = 15
 DEFAULT_SOUND_TIME = 16
 DEFAULT_VIBRATION_TIME = 10
+NO_VIBRATION_TIME = -1
+NO_SOUND_TIME = -1
 DEFAULT_OFFSET = 1
 EXIT_ALERT_LIST = ["Got it", "OK", "Hide", "TO CLOSE", "Yes"]
 CMD_DND_OFF = "cmd notification set_dnd off"
@@ -752,6 +763,7 @@
 OMAN = "oman"
 PERU_ENTEL = "peru_entel"
 PERU_TELEFONICA = "peru_telefonica"
+SPAIN_TELEFONICA = "spain_telefonica"
 PUERTORICO = "puertorico"
 ROMANIA = "romania"
 SAUDIARABIA = "saudiarabia"
@@ -762,7 +774,39 @@
 US_ATT = "us_att"
 US_TMO = "us_tmo"
 US_VZW = "us_vzw"
-
+MEXICO = "mexico"
+BAHAMAS = "bahamas"
+UK_EE = "uk_ee"
+COLUMBIA_TELEFONICA = "columbia_telefonica"
+JAPAN_EMOBILE = "japan_emobile"
+JAPAN_WIRELESSCITYPLANNING ="japan_wirelesscityplanning"
+JAPAN_DOCOMO = "japan_docomo"
+JAPAN_RAKUTEN = "japan_rakuten"
+KOREA_SKT = "korea_skt"
+KOREA_LGU = "korea_lgu"
+VENEZUELA = "venezuela"
+RUSSIA = "russia"
+RUSSIA_MEGAFON = "russia_megafon"
+TURKEY = "turkey"
+US = "us"
+US_SPRINT = "us_sprint"
+US_USC = "us_usc"
+AZERBAIJAN = "azerbaijan"
+CHINA = "china"
+SOUTHAFRICA_TELKOM = 'southafrica_telkom'
+GUATEMALA_TELEFONICA = "guatemala_telefonica"
+INDIA = "india"
+HUNGARY_TELEKOM = "hungary_telekom"
+CROATIA_HRVATSKI = "croatia_hrvatski"
+CZECH_TMOBILE = "czech_tmobile"
+SLOVAKIA_TELEKOM = "slovakia_telekom"
+AUSTRIA_MAGENTA = "austria_magenta"
+POLAND_TMOBILE = "poland_tmobile"
+AUSTRIA_TMOBILE = "austria_tmobile"
+MACEDONIA_TELEKOM = "macedonia_telekom"
+MONTENEGRO_TELEKOM = "montenegro_telekom"
+UKRAINE = "ukraine"
+NORWAY = "norway"
 # Carrier Config Update
 CARRIER_ID_VERSION = "3"
 ER_DB_ID_VERSION = "99999"
@@ -984,6 +1028,16 @@
     DEFAULT_WFC_IMS_ROAMING_MODE_INT = "carrier_default_wfc_ims_roaming_mode_int"
 
 
+class SimSlotInfo(enum.Enum):
+    """Mapping table of SIM_SLOT.
+
+    [SIM_SLOT_ID, PORT_ID, PHYSICAL_SLOT_ID]
+    """
+    SLOT_0 = [0, 0, 1]
+    SLOT_1 = [1, 0, 0]
+    SLOT_2 = [2, 1, 0]
+
+
 """
 End shared constant define for both Python and Java
 """
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 e9279be..6a9dd27 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
@@ -23,12 +23,15 @@
 from acts import tracelogger
 from acts.controllers.android_device import AndroidDevice
 from acts.utils import rand_ascii_str
+from acts.libs.proc.job import TimeoutError
 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 SimSlotInfo
 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_IN_CALL
 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
@@ -57,6 +60,7 @@
 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 change_slot
 from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
 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
@@ -83,21 +87,28 @@
 def dsds_dds_swap_message_streaming_test(
     log: tracelogger.TraceLogger,
     ads: Sequence[AndroidDevice],
-    test_rat: list,
-    test_slot: list,
-    init_dds: int,
+    sim_slot: Sequence[SimSlotInfo],
+    test_rat: Sequence[str],
+    test_slot: Sequence[SimSlotInfo] = [
+        SimSlotInfo.SLOT_0,
+        SimSlotInfo.SLOT_1,
+        SimSlotInfo.SLOT_0],
+    init_dds: int = 0,
     msg_type: str = "SMS",
     direction: str = "mt",
     streaming: bool = True,
     expected_result: bool = True) -> bool:
-    """Make MO and MT message at specific slot in specific RAT with DDS at
+    """Make MO/MT message at specific slot in specific RAT with DDS at
     specific slot and do the same steps after dds swap.
 
     Args:
         log: Logger object.
         ads: A list of Android device objects.
+        sim_slot: a list which contains 2 slots for logical slot 0 and 1.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1]
         test_rat: RAT for both slots of primary device.
         test_slot: The slot which make/receive MO/MT SMS/MMS of primary device.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_0]
         dds_slot: Preferred data slot of primary device.
         msg_type: SMS or MMS to send.
         direction: The direction of message("mo" or "mt") at first.
@@ -109,44 +120,33 @@
         TestFailure if failed.
     """
     result = True
+    to_change_slot = True
+    dds_slot = [sim.value[0] for sim in sim_slot]
+    if init_dds != dds_slot[0]:
+        dds_slot = dds_slot[::-1]
+        dds_slot.append(dds_slot[0])
+    else:
+        dds_slot.append(init_dds)
 
-    for test_slot, dds_slot in zip(test_slot, [init_dds, 1-init_dds]):
+    for test_slot, dds_slot in zip(test_slot, dds_slot):
         ads[0].log.info("test_slot: %d, dds_slot: %d", test_slot, dds_slot)
         result = result and dsds_message_streaming_test(
             log=log,
             ads=ads,
+            sim_slot=sim_slot,
             test_rat=test_rat,
             test_slot=test_slot,
             dds_slot=dds_slot,
             msg_type=msg_type,
             direction=direction,
+            to_change_slot=to_change_slot,
             streaming=streaming,
             expected_result=expected_result
         )
+        to_change_slot = False
         if not result:
             return result
 
-    log.info("Switch DDS back.")
-    if not set_dds_on_slot(ads[0], init_dds):
-        ads[0].log.error(
-            "Failed to set DDS at slot %s on %s",(init_dds, ads[0].serial))
-        return False
-
-    log.info("Check phones is in desired RAT.")
-    phone_setup_on_rat(
-        log,
-        ads[0],
-        test_rat[test_slot],
-        get_subid_from_slot_index(log, ads[0], init_dds)
-    )
-
-    log.info("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.")
-
     return result
 
 
@@ -154,11 +154,15 @@
     log: tracelogger.TraceLogger,
     tel_logger: TelephonyMetricLogger.for_test_case,
     ads: Sequence[AndroidDevice],
-    test_rat: list,
-    test_slot: list,
-    init_dds: int,
-    direction: str = "mo",
-    duration: int = 360,
+    sim_slot: Sequence[SimSlotInfo],
+    test_rat: Sequence[str],
+    init_dds: int = 0,
+    test_slot: Sequence[SimSlotInfo] = [
+        SimSlotInfo.SLOT_0,
+        SimSlotInfo.SLOT_1,
+        SimSlotInfo.SLOT_0],
+    direction: Optional[str] = None,
+    duration: int = WAIT_TIME_IN_CALL,
     streaming: bool = True,
     is_airplane_mode: bool = False,
     wfc_mode: Sequence[str] = [
@@ -175,10 +179,14 @@
         log: Logger object.
         tel_logger: Logger object for telephony proto.
         ads: A list of Android device objects.
+        sim_slot: a list which contains 2 slots for logical slot 0 and 1.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1]
         test_rat: RAT for both slots of primary device.
-        test_slot: The slot which make/receive MO/MT call of primary device.
         init_dds: Initial preferred data slot of primary device.
+        test_slot: The slot which make/receive MO/MT call of primary device.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_0]
         direction: The direction of call("mo" or "mt").
+        duration: In call time of voice call.
         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.
@@ -193,18 +201,27 @@
         TestFailure if failed.
     """
     result = True
+    to_change_slot = True
+    dds_slot = [sim.value[0] for sim in sim_slot]
+    if init_dds != dds_slot[0]:
+        dds_slot = dds_slot[::-1]
+        dds_slot.append(dds_slot[0])
+    else:
+        dds_slot.append(init_dds)
 
-    for test_slot, dds_slot in zip(test_slot, [init_dds, 1-init_dds]):
+    for test_slot, dds_slot in zip(test_slot, dds_slot):
         ads[0].log.info("test_slot: %d, dds_slot: %d", test_slot, dds_slot)
-        result = result and dsds_long_call_streaming_test(
+        result = result and dsds_call_streaming_test(
             log=log,
             tel_logger=tel_logger,
             ads=ads,
+            sim_slot=sim_slot,
             test_rat=test_rat,
-            test_slot=test_slot,
             dds_slot=dds_slot,
+            test_slot=test_slot,
             direction=direction,
             duration=duration,
+            to_change_slot=to_change_slot,
             streaming=streaming,
             is_airplane_mode=is_airplane_mode,
             wfc_mode=wfc_mode,
@@ -213,42 +230,24 @@
             turn_off_wifi_in_the_end=turn_off_wifi_in_the_end,
             turn_off_airplane_mode_in_the_end=turn_off_airplane_mode_in_the_end
         )
+        to_change_slot = False
         if not result:
             return result
 
-    log.info("Switch DDS back.")
-    if not set_dds_on_slot(ads[0], init_dds):
-        ads[0].log.error(
-            "Failed to set DDS at slot %s on %s",(init_dds, ads[0].serial))
-        return False
-
-    log.info("Check phones is in desired RAT.")
-    phone_setup_on_rat(
-        log,
-        ads[0],
-        test_rat[test_slot],
-        get_subid_from_slot_index(log, ads[0], init_dds)
-    )
-
-    log.info("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.")
-
     return result
 
 
-def dsds_long_call_streaming_test(
+def dsds_call_streaming_test(
     log: tracelogger.TraceLogger,
     tel_logger: TelephonyMetricLogger.for_test_case,
     ads: Sequence[AndroidDevice],
-    test_rat: list,
-    test_slot: int,
+    sim_slot: Sequence[SimSlotInfo],
+    test_rat: Sequence[str],
     dds_slot: int,
-    direction: str = "mo",
-    duration: int = 360,
+    test_slot: Optional[SimSlotInfo] = None,
+    direction: Optional[str] = None,
+    duration: int = WAIT_TIME_IN_CALL,
+    to_change_slot: bool = True,
     streaming: bool = True,
     is_airplane_mode: bool = False,
     wfc_mode: Sequence[str] = [
@@ -265,10 +264,21 @@
         log: Logger object.
         tel_logger: Logger object for telephony proto.
         ads: A list of Android device objects.
+        sim_slot: a list which contains 2 slots for logical slot 0 and 1.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1]
         test_rat: RAT for both slots of primary device.
-        test_slot: The slot which make/receive MO/MT call of primary device.
+            e.g. ["5g_volte", "5g_volte"]
         dds_slot: Preferred data slot of primary device.
+                  0 for pSIM,
+                  1 for eSIM port 0,
+                  2 for eSIM port 1.
+        test_slot: The slot which make/receive MO/MT call of primary device.
+            e.g. SimSlotInfo.SLOT_0 for pSIM
+                 SimSlotInfo.SLOT_1 for eSIM port 0
+                 SimSlotInfo.SLOT_2 for eSIM port 1
         direction: The direction of call("mo" or "mt").
+        duration: In call time of voice call.
+        to_change_slot: True to change slot, False otherwise.
         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.
@@ -282,6 +292,16 @@
     Returns:
         TestFailure if failed.
     """
+    rat_dict = dict((x, y) for x, y in list(zip(sim_slot, test_rat)))
+    wfc_mode_dict = dict((x, y) for x, y in list(zip(sim_slot, wfc_mode)))
+
+    if to_change_slot:
+        log.info("Step 0: Switch to specific SIM slot combination.")
+        try:
+            change_slot(ads[0], sim_slot)
+        except TimeoutError:
+            ads[0].log.warning("Device not support MEP.")
+
     log.info("Step 1: Switch DDS.")
     if not set_dds_on_slot(ads[0], dds_slot):
         ads[0].log.error(
@@ -296,18 +316,20 @@
         ads[0].log.info("Verify http connection successfully.")
 
     log.info("Step 3: Set up phones in desired RAT.")
+    if test_slot:
+        non_test_slot = list(set(sim_slot) - set([test_slot]))[0]
     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)
+        mo_sub_id = get_subid_from_slot_index(log, ad_mo, test_slot.value[0])
         if mo_sub_id == INVALID_SUB_ID:
-            ad_mo.log.warning("Failed to get sub ID at slot %s.", test_slot)
+            ad_mo.log.warning("Failed to get sub ID at slot %s.", test_slot.value[0])
             return False
         mo_other_sub_id = get_subid_from_slot_index(
-            log, ad_mo, 1-test_slot)
+            log, ad_mo, non_test_slot.value[0])
         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,
+        ad_mo.log.info("Sub ID for outgoing call at slot %s: %s", test_slot.value[0],
         get_outgoing_voice_sub_id(ad_mo))
 
         # setup voice subid on secondary device.
@@ -316,6 +338,7 @@
         if mt_sub_id == INVALID_SUB_ID:
             ad_mt.log.warning("Failed to get sub ID at default voice slot.")
             return False
+        mo_slot = test_slot.value[0]
         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,
@@ -325,39 +348,39 @@
         phone_setup_on_rat(
             log,
             ad_mo,
-            test_rat[1-test_slot],
+            rat_dict[non_test_slot],
             mo_other_sub_id,
             is_airplane_mode,
-            wfc_mode[1-test_slot],
+            wfc_mode_dict[non_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],
+            rat_dict[test_slot],
             mo_sub_id,
             is_airplane_mode,
-            wfc_mode[test_slot],
+            wfc_mode_dict[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)
+            log, ad_mo, rat_dict[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:
+    elif direction == "mt":
         # setup voice subid on primary device.
         ad_mt = ads[0]
-        mt_sub_id = get_subid_from_slot_index(log, ad_mt, test_slot)
+        mt_sub_id = get_subid_from_slot_index(log, ad_mt, test_slot.value[0])
         if mt_sub_id == INVALID_SUB_ID:
-            ad_mt.log.warning("Failed to get sub ID at slot %s.", test_slot)
+            ad_mt.log.warning("Failed to get sub ID at slot %s.", test_slot.value[0])
             return False
         mt_other_sub_id = get_subid_from_slot_index(
-            log, ad_mt, 1-test_slot)
+            log, ad_mt, non_test_slot.value[0])
         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,
+        ad_mt.log.info("Sub ID for incoming call at slot %s: %s", test_slot.value[0],
         get_outgoing_voice_sub_id(ad_mt))
 
         # setup voice subid on secondary device.
@@ -367,6 +390,7 @@
             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)
+        mt_slot = test_slot.value[0]
         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))
@@ -375,27 +399,29 @@
         phone_setup_on_rat(
             log,
             ad_mt,
-            test_rat[1-test_slot],
+            rat_dict[non_test_slot],
             mt_other_sub_id,
             is_airplane_mode,
-            wfc_mode[1-test_slot],
+            wfc_mode_dict[non_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],
+            rat_dict[test_slot],
             mt_sub_id,
             is_airplane_mode,
-            wfc_mode[test_slot],
+            wfc_mode_dict[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)
+            log, ad_mt, rat_dict[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)
+    else:
+        return True
 
     tasks = [(phone_setup_on_rat, mo_phone_setup_func_argv),
              (phone_setup_on_rat, mt_phone_setup_func_argv)]
@@ -438,7 +464,7 @@
 
     # 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]]
+    rat_list = [rat_dict[test_slot], rat_dict[non_test_slot]]
 
     if turn_off_wifi_in_the_end:
         log.info("Step 5-2: Turning off Wi-Fi......")
@@ -712,14 +738,16 @@
 def dsds_message_streaming_test(
     log: tracelogger.TraceLogger,
     ads: Sequence[AndroidDevice],
-    test_rat: list,
-    test_slot: int,
+    sim_slot: Sequence[SimSlotInfo],
+    test_rat: Sequence[str],
+    test_slot: SimSlotInfo,
     dds_slot: int,
     msg_type: str = "SMS",
     direction: str = "mt",
+    to_change_slot: bool = True,
     streaming: bool = True,
     expected_result: bool = True) -> bool:
-    """Make MO and MT SMS/MMS at specific slot in specific RAT with DDS at
+    """Make MO or MT SMS/MMS at specific slot in specific RAT with DDS at
     specific slot.
 
     Test step:
@@ -732,11 +760,21 @@
     Args:
         log: Logger object.
         ads: A list of Android device objects.
+        sim_slot: a list which contains 2 slots for logical slot 0 and 1.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1]
         test_rat: RAT for both slots of primary device.
-        test_slot: The slot which make/receive MO/MT SMS/MMS of primary device.
+            e.g. ["5g_volte", "5g_volte"]
+        test_slot: The slot which make/receive MO/MT call of primary device.
+            e.g. SimSlotInfo.SLOT_0 for pSIM
+                 SimSlotInfo.SLOT_1 for eSIM port 0
+                 SimSlotInfo.SLOT_2 for eSIM port 1
         dds_slot: Preferred data slot of primary device.
+                  0 for pSIM,
+                  1 for eSIM port 0,
+                  2 for eSIM port 1.
         msg_type: SMS or MMS to send.
         direction: The direction of message("mo" or "mt") at first.
+        to_change_slot: True to change slot, False otherwise.
         streaming: True for playing Youtube before send/receive SMS/MMS and
             False on the contrary.
         expected_result: True or False
@@ -744,6 +782,15 @@
     Returns:
         TestFailure if failed.
     """
+    rat_dict = dict((x, y) for x, y in list(zip(sim_slot, test_rat)))
+
+    if to_change_slot:
+        log.info("Step 0: Switch to specific SIM slot combination.")
+        try:
+            change_slot(ads[0], sim_slot)
+        except TimeoutError:
+            ads[0].log.warning("Device not support MEP.")
+
     log.info("Step 1: Switch DDS.")
     if not set_dds_on_slot(ads[0], dds_slot):
         ads[0].log.error(
@@ -758,18 +805,19 @@
         ads[0].log.info("Verify http connection successfully.")
 
     log.info("Step 3: Set up phones in desired RAT.")
+    non_test_slot = list(set(sim_slot) - set([test_slot]))[0]
     if direction == "mo":
         # setup message subid on primary device.
         ad_mo = ads[0]
-        mo_sub_id = get_subid_from_slot_index(log, ad_mo, test_slot)
+        mo_sub_id = get_subid_from_slot_index(log, ad_mo, test_slot.value[0])
         if mo_sub_id == INVALID_SUB_ID:
-            ad_mo.log.warning("Failed to get sub ID at slot %s.", test_slot)
+            ad_mo.log.warning("Failed to get sub ID at slot %s.", test_slot.value[0])
             return False
         mo_other_sub_id = get_subid_from_slot_index(
-            log, ad_mo, 1-test_slot)
+            log, ad_mo, non_test_slot.value[0])
         sub_id_list = [mo_sub_id, mo_other_sub_id]
         set_message_subid(ad_mo, mo_sub_id)
-        ad_mo.log.info("Sub ID for outgoing call at slot %s: %s", test_slot,
+        ad_mo.log.info("Sub ID for outgoing call at slot %s: %s", test_slot.value[0],
             get_outgoing_message_sub_id(ad_mo))
 
         # setup message subid on secondary device.
@@ -787,26 +835,27 @@
         phone_setup_on_rat(
             log,
             ad_mo,
-            test_rat[1-test_slot],
+            rat_dict[non_test_slot],
             mo_other_sub_id)
         # assign phone setup argv for test slot.
         mo_phone_setup_func_argv = (
             log,
             ad_mo,
-            test_rat[test_slot],
+            rat_dict[test_slot],
             mo_sub_id)
+        mt_phone_setup_func_argv = (log, ad_mt, 'general')
     else:
         # setup message subid on primary device.
         ad_mt = ads[0]
-        mt_sub_id = get_subid_from_slot_index(log, ad_mt, test_slot)
+        mt_sub_id = get_subid_from_slot_index(log, ad_mt, test_slot.value[0])
         if mt_sub_id == INVALID_SUB_ID:
-            ad_mt.log.warning("Failed to get sub ID at slot %s.", test_slot)
+            ad_mt.log.warning("Failed to get sub ID at slot %s.", test_slot.value[0])
             return False
         mt_other_sub_id = get_subid_from_slot_index(
-            log, ad_mt, 1-test_slot)
+            log, ad_mt, non_test_slot.value[0])
         sub_id_list = [mt_sub_id, mt_other_sub_id]
         set_message_subid(ad_mt, mt_sub_id)
-        ad_mt.log.info("Sub ID for incoming call at slot %s: %s", test_slot,
+        ad_mt.log.info("Sub ID for incoming call at slot %s: %s", test_slot.value[0],
             get_outgoing_message_sub_id(ad_mt))
 
         # setup message subid on secondary device.
@@ -824,13 +873,13 @@
         phone_setup_on_rat(
             log,
             ad_mt,
-            test_rat[1-test_slot],
+            rat_dict[non_test_slot],
             mt_other_sub_id)
         # assign phone setup argv for test slot.
         mt_phone_setup_func_argv = (
             log,
             ad_mt,
-            test_rat[test_slot],
+            rat_dict[test_slot],
             mt_sub_id)
         mo_phone_setup_func_argv = (log, ad_mo, 'general')
 
@@ -866,24 +915,15 @@
                     current_msg_sub_id)
                 expected_result = False
 
-    result_first = msim_message_test(log, ad_mo, ad_mt, mo_sub_id, mt_sub_id,
+    result = msim_message_test(log, ad_mo, ad_mt, mo_sub_id, mt_sub_id,
         msg=msg_type, expected_result=expected_result)
 
-    if not result_first:
+    if not result:
         log_messaging_screen_shot(ad_mo, test_name="%s_tx" % msg_type)
         log_messaging_screen_shot(ad_mt, test_name="%s_rx" % msg_type)
 
-    result_second = msim_message_test(log, ad_mt, ad_mo, mt_sub_id, mo_sub_id,
-        msg=msg_type, expected_result=expected_result)
-
-    if not result_second:
-        log_messaging_screen_shot(ad_mt, test_name="%s_tx" % msg_type)
-        log_messaging_screen_shot(ad_mo, test_name="%s_rx" % msg_type)
-
-    result = result_first and result_second
-
     log.info("Step 5: Verify RAT and HTTP connection.")
-    rat_list = [test_rat[test_slot], test_rat[1-test_slot]]
+    rat_list = [rat_dict[test_slot], rat_dict[non_test_slot]]
     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(
@@ -1084,6 +1124,8 @@
         log: logger object
         tel_logger: logger object for telephony proto
         ads: list of android devices
+        sim_slot: a list which contains 2 slots for logical slot 0 and 1.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1]
         nw_rat: RAT for both slots of the primary device
         call_slot: Slot for making voice call
         call_direction: "mo" or "mt" or None to stoping making call.
@@ -1833,6 +1875,7 @@
         callee_slot,
         forwarded_callee_slot,
         dds_slot,
+        sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
         caller_rat=["", ""],
         callee_rat=["", ""],
         forwarded_callee_rat=["", ""],
@@ -1859,6 +1902,8 @@
                         (0 or 1)
         forwarded_callee_slot: Slot of 3rd device receiving forwarded call.
         dds_slot: Preferred data slot
+        sim_slot: a list which contains 2 slots for logical slot 0 and 1.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1]
         caller_rat: RAT for both slots of the 2nd device
         callee_rat: RAT for both slots of the primary device
         forwarded_callee_rat: RAT for both slots of the 3rd device
@@ -1875,6 +1920,12 @@
     ad_callee = ads[0]
     ad_forwarded_callee = ads[2]
 
+    log.info("Step 0: Switch to specific SIM slot combination.")
+    try:
+        change_slot(ad_callee, sim_slot)
+    except TimeoutError:
+        ad_callee.log.warning("Device not support MEP.")
+
     if callee_slot is not None:
         callee_sub_id = get_subid_from_slot_index(
             log, ad_callee, callee_slot)
@@ -2107,6 +2158,7 @@
         p1_slot,
         p2_slot,
         dds_slot,
+        sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
         host_rat=["volte", "volte"],
         p1_rat="",
         p2_rat="",
@@ -2132,6 +2184,8 @@
         p1_slot: Slot on the participant device for the call
         p2_slot: Slot on another participant device for the call
         dds_slot: Preferred data slot
+        sim_slot: a list which contains 2 slots for logical slot 0 and 1.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1]
         host_rat: RAT for both slots of the primary device
         p1_rat: RAT for both slots of the participant device
         p2_rat: RAT for both slots of another participant device
@@ -2147,6 +2201,12 @@
     ad_p1 = ads[1]
     ad_p2 = ads[2]
 
+    log.info("Step 0: Switch to specific SIM slot combination.")
+    try:
+        change_slot(ad_host, sim_slot)
+    except TimeoutError:
+        ad_host.log.warning("Device not support MEP.")
+
     if host_slot is not None:
         host_sub_id = get_subid_from_slot_index(
             log, ad_host, host_slot)
@@ -2537,6 +2597,7 @@
         ads,
         host_slot,
         dds_slot,
+        sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
         host_rat=["5g_wfc", "5g_wfc"],
         merge=True,
         disable_cw=False,
@@ -2566,6 +2627,8 @@
         host_slot: Slot on the primary device to host the comference call.
                     0 or 1 (0 for pSIM or 1 for eSIM)call
         dds_slot: Preferred data slot
+        sim_slot: a list which contains 2 slots for logical slot 0 and 1.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1]
         host_rat: RAT for both slots of the primary devicevice
         merge: True for merging 2 calls into the conference call. False for
                 not merging 2 separated call.
@@ -2589,6 +2652,12 @@
     ad_p1 = ads[1]
     ad_p2 = ads[2]
 
+    log.info("Step 0: Switch to specific SIM slot combination.")
+    try:
+        change_slot(ad_host, sim_slot)
+    except TimeoutError:
+        ad_host.log.warning("Device not support MEP.")
+
     host_sub_id = get_subid_from_slot_index(log, ad_host, host_slot)
     if host_sub_id == INVALID_SUB_ID:
         ad_host.log.warning("Failed to get sub ID at slot.", host_slot)
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_ims_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_ims_utils.py
index 4001f9b..774c716 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_ims_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_ims_utils.py
@@ -394,8 +394,12 @@
                     "WFC is enabled for sub ID %s. Disabling WFC...", sub_id)
                 ad.droid.imsMmTelSetVoWiFiSettingEnabled(sub_id, False)
                 return True
-
+            operator_name = get_operator_name(ad.log, ad, sub_id)
             ad.log.info("Set wfc mode to %s for sub ID %s.", wfc_mode, sub_id)
+            ad.root_adb()
+            if CARRIER_ATT == operator_name:
+                ad.adb.shell("setprop dbg.att.force_wfc_nv_enabled true")
+            ad.adb.shell("setprop dbg.force_wfc_activated true")
             ad.droid.imsMmTelSetVoWiFiModeSetting(sub_id, wfc_mode)
             mode = ad.droid.imsMmTelGetVoWiFiModeSetting(sub_id)
             if mode != wfc_mode:
@@ -788,4 +792,4 @@
         Return False if timeout.
     """
     return _wait_for_droid_in_state(
-        log, ad, max_time, lambda log, ad: not is_wfc_enabled(log, ad))
\ No newline at end of file
+        log, ad, max_time, lambda log, ad: not is_wfc_enabled(log, ad))
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 8ab69f6..7341685 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
@@ -31,18 +31,18 @@
 
 _LS_MASK_NAME = "Lassen default + TCP"
 
-_LS_ENABLE_LOG_SHELL = f"""\
+_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 = """\
+-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"
-"""
-
+-a com.android.pixellogger.service.logging.LoggingService.ACTION_CONFIGURE_ALWAYS_ON_LOGGING \
+-e intent_key_enable "false"'
+_LS_GET_LOG_STATUS_SHELL = 'getprop vendor.sys.modem.logging.status'
+_LS_START_LS_TIMEOUT_SECS = 30
+_LS_STOP_LS_TIMEOUT_SECS = 30
 
 def check_if_tensor_platform(ad):
     """Check if current platform belongs to the Tensor platform
@@ -86,30 +86,32 @@
         return True
 
 
-def start_dsp_logger_p21(ad, retry=3):
-    """Start DSP logging for P21 devices.
+def start_dsp_logger(ad, p21 = False, retry = 3):
+    """Start DSP logging for P21/P22 devices.
 
     Args:
         ad: Android object.
+        p21: True if p21 device, False otherwise.
         retry: times of retry to enable DSP logger.
 
     Returns:
         True if DSP logger is enabled correctly. Otherwise False.
     """
-    if not getattr(ad, "dsp_log_p21", False): return
+    registry_name = "!LTEL1.HAL.DSP\\ clkgating\\ Enb/Dis" if p21 else "NASU.LCPU.LOG.SWITCH"
+    nv_value = "00" if p21 else "02"
 
     def _is_dsp_enabled(ad):
-        return "00" in ad.adb.shell('am instrument -w -e request '
-            'at+googgetnv=\\"\\!LTEL1\\.HAL\\.DSP\\ clkgating\\ Enb\\/Dis\\" '
-            '-e response wait "com.google.mdstest/com.google.mdstest.'
-            'instrument.ModemATCommandInstrumentation"')
+        return nv_value in ad.adb.shell('am instrument -w -e request '
+            f'at+googgetnv=\\"{registry_name}\\" -e response wait '
+            'com.google.mdstest/com.google.mdstest.instrument.'
+            'ModemATCommandInstrumentation')
 
     for _ in range(retry):
         if not _is_dsp_enabled(ad):
             ad.adb.shell('am instrument -w -e request at+googsetnv=\\"'
-                '\\!LTEL1\\.HAL\\.DSP\\ clkgating\\ Enb\\/Dis\\"\\,0\\,\\"'
-                '00\\" -e response wait "com.google.mdstest/com.google.mdstest.'
-                'instrument.ModemATCommandInstrumentation"')
+                f'{registry_name}\\",0,\\"{nv_value}\\" -e response wait '
+                'com.google.mdstest/com.google.mdstest.instrument.'
+                'ModemATCommandInstrumentation')
             time.sleep(3)
         else:
             ad.log.info("DSP logger is enabled, reboot to start.")
@@ -119,6 +121,15 @@
     return False
 
 
+def is_sdm_logger_running(ad):
+    """Queries the status of SDM logger.
+
+    Returns:
+      True if the SDM logger is runninng.
+    """
+    return "true" in ad.adb.shell(_LS_GET_LOG_STATUS_SHELL, ignore_status=True)
+
+
 def start_sdm_logger(ad):
     """Start SDM logger."""
     if not getattr(ad, "sdm_log", True): return
@@ -134,39 +145,36 @@
             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
-    ad.log.debug("start sdm logging")
-    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):
+    if not is_sdm_logger_running(ad):
+        ad.log.debug("starting sdm logger...")
         ad.adb.shell(_LS_ENABLE_LOG_SHELL, ignore_status=True)
-        time.sleep(5)
+
+        timeout = time.monotonic() + _LS_START_LS_TIMEOUT_SECS
+        while time.monotonic() < timeout:
+            time.sleep(1)
+            if is_sdm_logger_running(ad):
+                ad.log.info('SDM logger has started')
+                break
+        else:
+            raise RuntimeError(
+                'Timed out while waiting for SDM logger to start.')
 
 
 def stop_sdm_logger(ad):
     """Stop SDM logger."""
-    ad.sdm_log_path = DEFAULT_SDM_LOG_PATH
-    cycle = 1
-
-    ad.log.debug("stop sdm logging")
-    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)
+    if is_sdm_logger_running(ad):
+        ad.log.info("Stopping SDM logger...")
         ad.adb.shell(_LS_DISABLE_LOG_SHELL, ignore_status=True)
-        cycle += 1
-        time.sleep(15)
+
+        timeout = time.monotonic() + _LS_STOP_LS_TIMEOUT_SECS
+        while time.monotonic() < timeout:
+            time.sleep(1)
+            if not is_sdm_logger_running(ad):
+                ad.log.info('SDM logger has stoped')
+                break
+        else:
+            raise RuntimeError(
+                'Timed out while waiting for SDM logger to stop.')
 
 
 def start_sdm_loggers(log, ads):
@@ -382,9 +390,7 @@
             ad.log.info("Kill %s" % qxdm_logger_apk)
             ad.force_stop_apk(qxdm_logger_apk)
             time.sleep(5)
-    for perm in ("READ",):
-        ad.adb.shell("pm grant %s android.permission.%s_EXTERNAL_STORAGE" %
-                     (qxdm_logger_apk, perm))
+    ad.adb.shell("pm grant %s android.permission.READ_EXTERNAL_STORAGE" % (qxdm_logger_apk))
     time.sleep(2)
     for i in range(3):
         ad.unlock_screen()
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 11c05dd..bc56159 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
@@ -159,6 +159,12 @@
     verify_caller_func = None
     verify_callee_func = None
 
+    if long_msg:
+      for ad in [ad_mo, ad_mt]:
+        ad.root_adb()
+        # set max sms length to 10000 for long sms test
+        ad.adb.shell("settings put global sms_outgoing_check_max_count 10000")
+
     if mo_rat:
         mo_phone_setup_argv = (
             log,
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_ops_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_ops_utils.py
index 1e68c6d..40cf625 100644
--- a/acts_tests/acts_contrib/test_utils/tel/tel_ops_utils.py
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_ops_utils.py
@@ -14,22 +14,14 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import time
-from acts_contrib.test_utils.net import ui_utils
-from acts_contrib.test_utils.tel.tel_defines import MOBILE_DATA
-from acts_contrib.test_utils.tel.tel_defines import USE_SIM
 from acts_contrib.test_utils.tel.tel_data_utils import active_file_download_task
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_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 wait_and_answer_call
 
 
-def initiate_call_verify_operation(log,
-                                    caller,
-                                    callee,
-                                    download=False):
+def initiate_call_verify_operation(log, caller, callee, download=False):
     """Initiate call and verify operations with an option of data idle or data download
 
     Args:
@@ -42,16 +34,17 @@
         True: if call initiated and verified operations successfully
         False: for errors
     """
-    caller_number = caller.telephony['subscription'][
-        get_outgoing_voice_sub_id(caller)]['phone_num']
-    callee_number = callee.telephony['subscription'][
-        get_outgoing_voice_sub_id(callee)]['phone_num']
+    caller_number = caller.telephony['subscription'][get_outgoing_voice_sub_id(
+        caller)]['phone_num']
+    callee_number = callee.telephony['subscription'][get_outgoing_voice_sub_id(
+        callee)]['phone_num']
     if not initiate_call(log, caller, callee_number):
         caller.log.error("Phone was unable to initate a call")
         return False
 
     if not wait_and_answer_call(log, callee, caller_number):
-        callee.log.error("Callee failed to receive incoming call or answered the call.")
+        callee.log.error(
+            "Callee failed to receive incoming call or answered the call.")
         return False
 
     if download:
@@ -63,55 +56,3 @@
         caller.log.error("Unable to hang up the call")
         return False
     return True
-
-def get_resource_value(ad, label_text= None):
-    """Get current resource value
-
-    Args:
-        ad:  android device object as caller.
-        label_text: Enter text to be detected
-
-    Return:
-        node attribute value
-    """
-    if label_text == USE_SIM:
-        resource_id = 'android:id/switch_widget'
-        label_resource_id = 'com.android.settings:id/switch_text'
-        node_attribute = 'checked'
-    elif label_text == MOBILE_DATA:
-        resource_id = 'android:id/switch_widget'
-        label_resource_id = 'android:id/widget_frame'
-        label_text = ''
-        node_attribute = 'checked'
-    else:
-        ad.log.error(
-            'Missing arguments, resource_id, label_text and node_attribute'
-            )
-
-    resource = {
-        'resource_id': resource_id,
-    }
-    node = ui_utils.wait_and_get_xml_node(ad,
-                                        timeout=30,
-                                        sibling=resource,
-                                        text=label_text,
-                                        resource_id=label_resource_id)
-    return node.attributes[node_attribute].value
-
-def wait_and_click_element(ad, label_text=None, label_resource_id=None):
-    """Wait for a UI element to appear and click on it.
-
-    This function locates a UI element on the screen by matching attributes of
-    nodes in XML DOM, calculates a point's coordinates within the boundary of the
-    element, and clicks on the point marked by the coordinates.
-
-  Args:
-    ad: AndroidDevice object.
-    label_text: Identify the key value parameter
-    label_text: Identifies the resource id
-  """
-    if label_resource_id is not None:
-        ui_utils.wait_and_click(ad, text=label_text, resource_id=label_resource_id)
-    else:
-        ui_utils.wait_and_click(ad, text=label_text)
-    time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
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
index 6077d9c..602c380 100644
--- 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
@@ -20,6 +20,7 @@
 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 CARRIER_VZW
 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
@@ -68,6 +69,7 @@
 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_subscription_utils import set_default_sub_for_all_services
 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
@@ -97,7 +99,8 @@
                       wfc_mode,
                       wifi_ssid=None,
                       wifi_pwd=None,
-                      nw_gen=None):
+                      nw_gen=None,
+                      nr_type=None):
     """Phone setup function for epdg call test.
     Set WFC mode according to wfc_mode.
     Set airplane mode according to is_airplane_mode.
@@ -114,13 +117,14 @@
         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.
     """
     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)
+                                              wifi_ssid, wifi_pwd, nw_gen, nr_type)
 
 
 def phone_setup_iwlan_for_subscription(log,
@@ -133,6 +137,7 @@
                                        nw_gen=None,
                                        nr_type=None):
     """Phone setup function for epdg call test for subscription id.
+    Enable VoLTE. (b/235019060#comment20)
     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.)
@@ -163,6 +168,7 @@
                 nr_type=nr_type):
             ad.log.error("Failed to set to %s data.", nw_gen)
             return False
+    toggle_volte_for_subscription(log, ad, sub_id, True)
     toggle_airplane_mode(log, ad, is_airplane_mode, strict_checking=False)
 
     # Pause at least for 4 seconds is necessary after airplane mode was turned
@@ -445,8 +451,10 @@
             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 ad.telephony["subscription"][sub_id]["operator"] != CARRIER_VZW \
+            and ad.telephony["subscription"][sub_id]["operator"] != CARRIER_TMO :
+        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):
@@ -504,13 +512,10 @@
             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)
+    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)
 
@@ -1591,6 +1596,26 @@
             break
         else:
             ad.log.info("Did not find valid data or voice sub id")
+            if getattr(ad, 'mep', False):
+                default_slot = getattr(ad, "default_slot", 1)
+                if get_subid_from_slot_index(ad.log, ad, default_slot) != INVALID_SUB_ID:
+                    ad.log.info("Slot %s is the default slot.", default_slot)
+                    set_default_sub_for_all_services(ad, default_slot)
+                else:
+                    ad.log.warning("Slot %s is NOT a valid slot. Slot %s will be used by default.",
+                        default_slot, 1-default_slot)
+                    set_default_sub_for_all_services(ad, 1-default_slot)
+                    setattr(ad, "default_slot", 1-default_slot)
+            elif getattr(ad, 'dsds', False):
+                default_slot = getattr(ad, "default_slot", 0)
+                if get_subid_from_slot_index(ad.log, ad, default_slot) != INVALID_SUB_ID:
+                    ad.log.info("Slot %s is the default slot.", default_slot)
+                    set_default_sub_for_all_services(ad, default_slot)
+                else:
+                    ad.log.warning("Slot %s is NOT a valid slot. Slot %s will be used by default.",
+                        default_slot, 1-default_slot)
+                    set_default_sub_for_all_services(ad, 1-default_slot)
+                    setattr(ad, "default_slot", 1-default_slot)
             time.sleep(5)
             duration += 5
     else:
@@ -1626,12 +1651,14 @@
     Phone not in call.
     Phone have no stored WiFi network and WiFi disconnected.
     Phone not in airplane mode.
+    Phone is data on.
     """
     result = True
     if not toggle_airplane_mode(log, ad, False, False):
         ad.log.error("Fail to turn off airplane mode")
         result = False
     try:
+        ad.droid.telephonyToggleDataConnection(True)
         set_wifi_to_default(log, ad)
         while ad.droid.telecomIsInCall() and retry > 0:
             ad.droid.telecomEndCall()
@@ -1755,4 +1782,4 @@
     # 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
+    return True
diff --git a/acts_tests/acts_contrib/test_utils/tel/tel_settings_utils.py b/acts_tests/acts_contrib/test_utils/tel/tel_settings_utils.py
new file mode 100644
index 0000000..2f628dd
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/tel/tel_settings_utils.py
@@ -0,0 +1,492 @@
+#!/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.net import ui_utils
+from acts_contrib.test_utils.tel.tel_defines import CHIPSET_MODELS_LIST
+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 KEYEVENT_DEL
+from acts_contrib.test_utils.tel.tel_defines import MOBILE_DATA
+from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
+from acts_contrib.test_utils.tel.tel_defines import SCROLL_DOWN
+from acts_contrib.test_utils.tel.tel_defines import SCROLL_UP
+from acts_contrib.test_utils.tel.tel_defines import SLOW_SCROLL_DOWN
+from acts_contrib.test_utils.tel.tel_defines import USE_SIM
+from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
+from acts_contrib.test_utils.tel.tel_5g_test_utils import provision_device_for_5g
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_volte
+from acts_contrib.test_utils.tel.tel_test_utils import get_current_override_network_type
+from acts_contrib.test_utils.tel.tel_test_utils import is_droid_in_network_generation
+
+
+ATT_APN = {
+    'Name': 'NXTGENPHONE',
+    'APN': 'NXTGENPHONE',
+    'MMSC': 'http://mmsc.mobile.att.net',
+    'MMS proxy': 'proxy.mobile.att.net',
+    'MMS port': '80',
+    'MCC': '310',
+    'MNC': '410',
+    'APN type': 'default,mms,supi,hipri',
+    'APN protocol': 'IPv4',
+    'APN roaming protocol': 'IPv4',
+    'MVNO type': 'None'
+    }
+
+TMO_APN = {
+    'Name': 'TMOUS',
+    'APN': 'fast.t-mobile.com',
+    'MMSC': 'https://mms.msg.eng.t-mobile.com/mms/wapenc',
+    'MCC': '310',
+    'MNC': '260',
+    'APN type': 'default,supi,ia,mms,xcap',
+    'APN protocol': 'IPv6',
+    'APN roaming protocol': 'IPv4',
+    'MVNO type': 'None'
+    }
+
+TMO_BEARER = ['HSPA', 'EVDO_B', 'eHRPD', 'LTE', 'HSPAP', 'GPRS', 'EDGE', 'UMTS', '1xRTT', 'EVDO_0',
+            'EVDO_A', 'HSDPA', 'HSUPA', 'IS95A', 'IS95B','NR']
+
+
+def is_current_build_s(ad):
+    """Verify current build is S
+    Args:
+        ad: android device object.
+
+    Returns:
+        True: If Build is S
+        False: If Build is not S
+    """
+    build_id = ad.adb.shell('getprop ro.product.build.id')
+    s_build = False
+    if build_id[0] == 'S':
+       s_build = True
+    return s_build
+
+def launch_SIMs_settings(ad):
+    """Launch SIMs settings page
+    Args:
+        ad: android device object.
+    """
+    ad.adb.shell('am start -a android.settings.WIRELESS_SETTINGS')
+    ui_utils.wait_and_click(ad, text='SIMs')
+
+def get_resource_value(ad, label_text= None):
+    """Get current resource value
+
+    Args:
+        ad:  android device object.
+        label_text: Enter text to be detected
+
+    Return:
+        node attribute value
+    """
+    if label_text == USE_SIM:
+        resource_id = 'android:id/switch_widget'
+        label_resource_id = 'com.android.settings:id/switch_text'
+        node_attribute = 'checked'
+    elif label_text == MOBILE_DATA:
+        resource_id = 'android:id/switch_widget'
+        label_resource_id = 'android:id/widget_frame'
+        label_text = ''
+        node_attribute = 'checked'
+    elif label_text == 'MCC' or label_text == 'MNC':
+        resource_id = 'android:id/summary'
+        label_resource_id = 'android:id/title'
+        node_attribute = 'text'
+    else:
+        ad.log.error(
+            'Missing arguments, resource_id, label_text and node_attribute'
+            )
+
+    resource = {
+        'resource_id': resource_id,
+    }
+    node = ui_utils.wait_and_get_xml_node(ad,
+                                        timeout=30,
+                                        sibling=resource,
+                                        text=label_text,
+                                        resource_id=label_resource_id)
+    return node.attributes[node_attribute].value
+
+def toggle_sim_test(ad, nw_gen, nr_type=None):
+    """Disable and Enable SIM settings
+    Args:
+        ad:  android device object.
+        nw_gen: network generation the phone should be camped on.
+        nr_type: check NR network.
+    """
+    s_build = is_current_build_s(ad)
+    if nw_gen  == GEN_5G:
+        if not provision_device_for_5g(ad.log, ad, nr_type=nr_type):
+            return False
+    elif nw_gen == GEN_4G:
+        if not phone_setup_volte(ad.log, ad):
+            ad.log.error('Phone failed to enable LTE')
+            return False
+    launch_SIMs_settings(ad)
+
+    switch_value = get_resource_value(ad, USE_SIM)
+    if switch_value == 'true':
+        ad.log.info('SIM is enabled as expected')
+    else:
+        ad.log.error('SIM should be enabled but SIM is disabled')
+        return False
+
+    label_text = USE_SIM
+    label_resource_id = 'com.android.settings:id/switch_text'
+
+    ad.log.info('Start Disabling SIM')
+    ui_utils.wait_and_click(ad,
+                            text=label_text,
+                            resource_id=label_resource_id)
+
+    button_resource_id = 'android:id/button1'
+    if any(model in ad.model for model in CHIPSET_MODELS_LIST) and s_build:
+        ui_utils.wait_and_click(ad, text='YES', resource_id=button_resource_id)
+    else:
+        ui_utils.wait_and_click(ad, text='Yes', resource_id=button_resource_id)
+
+    switch_value = get_resource_value(ad, USE_SIM)
+    if switch_value == 'false':
+        ad.log.info('SIM is disabled as expected')
+    else:
+        ad.log.error('SIM should be disabled but SIM is enabled')
+        return False
+
+    ad.log.info('Start Enabling SIM')
+    ui_utils.wait_and_click(ad,
+                            text=label_text,
+                            resource_id=label_resource_id)
+
+    if any(model in ad.model for model in CHIPSET_MODELS_LIST) and s_build:
+        ui_utils.wait_and_click(ad, text='YES', resource_id=button_resource_id)
+    elif any(model in ad.model for model in CHIPSET_MODELS_LIST) and not s_build:
+        pass
+    else:
+        ui_utils.wait_and_click(ad, text='Yes', resource_id=button_resource_id)
+
+    switch_value = get_resource_value(ad, USE_SIM)
+    if switch_value == 'true':
+        ad.log.info('SIM is enabled as expected')
+    else:
+        ad.log.error('SIM should be enabled but SIM is disabled')
+        return False
+    time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+
+    if nw_gen  == GEN_5G:
+        if not is_current_network_5g(ad, nr_type=nr_type, timeout=60):
+            ad.log.error('Unable to connect on 5G network')
+            return False
+        ad.log.info('Success! attached on 5G')
+    elif nw_gen  == GEN_4G:
+        if not is_droid_in_network_generation(self.log, ad, GEN_4G,
+                                            NETWORK_SERVICE_DATA):
+            ad.log.error('Failure - expected LTE, current %s',
+                         get_current_override_network_type(ad))
+            return False
+        ad.log.info('Success! attached on LTE')
+    return True
+
+def toggle_mobile_data_test(ad, nw_gen, nr_type=None):
+    """Disable and Enable SIM settings
+    Args:
+        ad:  android device object.
+        nw_gen: network generation the phone should be camped on.
+        nr_type: check NR network.
+    """
+    if nw_gen  == GEN_5G:
+        if not provision_device_for_5g(ad.log, ad, nr_type=nr_type):
+            return False
+    elif nw_gen == GEN_4G:
+        if not phone_setup_volte(ad.log, ad):
+            ad.log.error('Phone failed to enable LTE')
+            return False
+
+    launch_SIMs_settings(ad)
+    switch_value = get_resource_value(ad, MOBILE_DATA)
+
+    if switch_value != 'true':
+        ad.log.error('Mobile data should be enabled but it is disabled')
+        return False
+    ad.log.info('Mobile data is enabled as expected')
+
+    ad.log.info('Start Disabling mobile data')
+
+    ad.droid.telephonyToggleDataConnection(False)
+    time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+
+    switch_value = get_resource_value(ad, MOBILE_DATA)
+    if switch_value != 'false':
+        ad.log.error('Mobile data should be disabled but it is enabled')
+        return False
+    ad.log.info('Mobile data is disabled as expected')
+
+    ad.log.info('Start Enabling mobile data')
+    ad.droid.telephonyToggleDataConnection(True)
+    time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+
+    switch_value = get_resource_value(ad, MOBILE_DATA)
+    if switch_value == 'true':
+        ad.log.error('Mobile data should be enabled but it is disabled')
+        return False
+    ad.log.info('Mobile data is enabled as expected')
+
+    if nw_gen  == GEN_5G:
+        if not is_current_network_5g(ad, nr_type=nr_type, timeout=60):
+            ad.log.error('Failure - expected NR_NSA, current %s',
+                     get_current_override_network_type(ad))
+        ad.log.info('Success! attached on 5g NSA')
+    elif nw_gen  == GEN_4G:
+        if not is_droid_in_network_generation(self.log, ad, GEN_4G,
+                                            NETWORK_SERVICE_DATA):
+            ad.log.error('Failure - expected LTE, current %s',
+                         get_current_override_network_type(ad))
+            return False
+        ad.log.info('Success! attached on LTE')
+    return True
+
+def verify_mcc_mnc_value(ad, current_value, expected_value, key):
+    """Verify MCC and MNC value
+
+    Args:
+        ad: Android device object.
+        current_value: Current value of property.
+        expected_value: Expected value of property.
+        key: Properties for APN settings either MCC or MNC.
+    """
+    if current_value != expected_value:
+        ad.log.info('Current %s value is %s, change it to %s'
+            % (key, current_mcc_value, expected_value))
+        ui_utils.wait_and_click(ad, text=key)
+        for _ in range(len(current_value)):
+            caller.adb.shell(KEYEVENT_DEL)
+        ui_utils.wait_and_input_text(ad, expected_value)
+        ui_utils.wait_and_click(caller, text='OK', resource_id='android:id/button1')
+    ad.log.info('Verified Current %s value matched with required value', key)
+
+def wait_and_input_value(ad, key, value):
+    """Enter input value to the key using UI
+    Args:
+        ad: Android device object.
+        key: Properties for APN settings.
+        value: Value to be entered for corresponding key.
+    """
+    ui_utils.wait_and_click(ad, text=key)
+    ad.log.info('Enter %s: %s' % (key, value))
+    ui_utils.wait_and_input_text(ad, value)
+    if key not in ['APN roaming protocol', 'APN protocol', 'MVNO type']:
+        ui_utils.wait_and_click(ad, text='OK', resource_id='android:id/button1')
+
+def att_apn_test(log, caller, callee, nw_gen, nr_type=None, msg_type=None):
+    """ATT APN Test
+
+    Args:
+        log: Log object.
+        caller: android device object as caller.
+        callee: android device object as callee.
+        nw_gen: network generation the phone should be camped on.
+        nr_type: check NR network.
+        msg_type: messaging type sms or mms
+    """
+    if nw_gen  == GEN_5G:
+        mo_rat='5g'
+        if not provision_device_for_5g(caller.log, caller, nr_type=nr_type):
+            return False
+    elif nw_gen == GEN_4G:
+        mo_rat='volte'
+        if not phone_setup_volte(caller.log, caller):
+            caller.log.error('Phone failed to enable LTE')
+            return False
+    else:
+        mo_rat='general'
+
+    launch_SIMs_settings(caller)
+
+    # Scroll down
+    if not ui_utils.has_element(caller, text='Access Point Names'):
+        for _ in range(3):
+            caller.adb.shell(SCROLL_DOWN)
+
+    ui_utils.wait_and_click(caller, text='Access Point Names')
+    ui_utils.wait_and_click(caller, content_desc='New APN')
+
+    wait_and_input_value(caller, 'Name', ATT_APN['Name'])
+    wait_and_input_value(caller, 'APN', ATT_APN['APN'])
+    wait_and_input_value(caller, 'MMSC', ATT_APN['MMSC'])
+
+    # Scroll down
+    caller.adb.shell(SCROLL_DOWN)
+
+    wait_and_input_value(caller, 'MMS proxy', ATT_APN['MMS proxy'])
+    wait_and_input_value(caller, 'MMS port', ATT_APN['MMS port'])
+
+    caller.log.info('Enter MCC value: %s', ATT_APN['MCC'])
+    current_mcc_value = get_resource_value(caller,'MCC')
+    verify_mcc_mnc_value(caller, current_mcc_value, ATT_APN['MCC'], 'MCC')
+
+    # Scroll down
+    caller.adb.shell(SCROLL_DOWN)
+
+    caller.log.info('Enter MNC value: %s', ATT_APN['MNC'])
+    current_mnc_value = get_resource_value(caller,'MNC')
+    verify_mcc_mnc_value(caller, current_mnc_value, ATT_APN['MNC'], 'MNC')
+
+    # Scroll down
+    caller.adb.shell(SCROLL_DOWN)
+
+    wait_and_input_value(caller, 'APN type', ATT_APN['APN type'])
+
+    # Scroll down
+    caller.adb.shell(SCROLL_DOWN)
+
+    wait_and_input_value(caller, 'APN protocol', ATT_APN['APN protocol'])
+
+    wait_and_input_value(caller, 'APN roaming protocol', ATT_APN['APN roaming protocol'])
+
+    # Scroll down
+    caller.adb.shell(SCROLL_DOWN)
+
+    wait_and_input_value(caller, 'MVNO type', ATT_APN['MVNO type'])
+
+    ui_utils.wait_and_click(caller, content_desc='More options')
+
+    ui_utils.wait_and_click(caller, text='Save', resource_id='android:id/title')
+
+    node = ui_utils.wait_and_get_xml_node(caller, timeout=30, text=ATT_APN['Name'])
+    bounds = node.parentNode.nextSibling.attributes['bounds'].value
+
+    ui_utils.wait_and_click(caller,
+                            text='',
+                            resource_id='com.android.settings:id/apn_radiobutton',
+                            bounds= bounds)
+    time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+
+    if msg_type is not None:
+        message_test(
+            log,
+            caller,
+            callee,
+            mo_rat=mo_rat,
+            mt_rat='general',
+            msg_type=msg_type)
+
+def tmo_apn_test(log, caller, callee, nw_gen, nr_type=None, msg_type=None):
+    """TMO APN Test
+
+    Args:
+        log: Log object.
+        caller: android device object as caller.
+        callee: android device object as callee.
+        nw_gen: network generation the phone should be camped on.
+        nr_type: check NR network.
+        msg_type: messaging type sms or mms
+    """
+    if nw_gen == GEN_5G:
+        mo_rat='5g'
+        if not provision_device_for_5g(caller.log, caller, nr_type=nr_type):
+            return False
+    elif nw_gen == GEN_4G:
+        mo_rat='volte'
+        if not phone_setup_volte(caller.log, caller):
+            caller.log.error('Phone failed to enable LTE')
+            return False
+    else:
+        mo_rat='general'
+
+    launch_SIMs_settings(caller)
+    time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+
+    if not ui_utils.has_element(caller, text='Access Point Names'):
+        for _ in range(5):
+            caller.adb.shell(SCROLL_DOWN)
+
+    ui_utils.wait_and_click(caller, text='Access Point Names')
+    ui_utils.wait_and_click(caller, content_desc='New APN')
+
+    wait_and_input_value(caller, 'Name', TMO_APN['Name'])
+    wait_and_input_value(caller, 'APN', TMO_APN['APN'])
+    wait_and_input_value(caller, 'MMSC', TMO_APN['MMSC'])
+
+    # Scroll down
+    caller.adb.shell(SLOW_SCROLL_DOWN)
+
+    caller.log.info('Enter MCC value: %s', TMO_APN['MCC'])
+    current_mcc_value = get_resource_value(caller,'MCC')
+    verify_mcc_mnc_value(caller, current_mcc_value, TMO_APN['MCC'], 'MCC')
+
+    # Scroll down
+    caller.adb.shell(SLOW_SCROLL_DOWN)
+
+    caller.log.info('Enter MNC value: %s', TMO_APN['MNC'])
+    current_mnc_value = get_resource_value(caller,'MNC')
+    verify_mcc_mnc_value(caller, current_mnc_value, TMO_APN['MNC'], 'MNC')
+
+    wait_and_input_value(caller, 'APN type', TMO_APN['APN type'])
+
+    # Scroll down
+    caller.adb.shell(SLOW_SCROLL_DOWN)
+
+    wait_and_input_value(caller, 'APN protocol', TMO_APN['APN protocol'])
+    wait_and_input_value(caller, 'APN roaming protocol', TMO_APN['APN roaming protocol'])
+
+    wait_and_input_value(caller, 'MVNO type', TMO_APN['MVNO type'])
+
+    caller.log.info('Click following supported network: %s', TMO_BEARER)
+    ui_utils.wait_and_click(caller, text='Bearer')
+    for network in TMO_BEARER:
+        if ((network == 'NR') or
+            (network == 'IS95B') or
+            (network ==  'IS95A') or
+            (network ==  'EVDO_0') or
+            (network ==  '1xRTT')):
+            # Scroll down
+            caller.adb.shell(SLOW_SCROLL_DOWN)
+            ui_utils.wait_and_click(caller, text=network)
+            caller.log.info('Enabled %s', network)
+            time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+            # Scroll up
+            caller.adb.shell(SCROLL_UP)
+        else:
+            ui_utils.wait_and_click(caller, text=network)
+            caller.log.info('Enabled %s', network)
+        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+
+    ui_utils.wait_and_click(caller, text='OK', resource_id='android:id/button1')
+
+    ui_utils.wait_and_click(caller, content_desc='More options')
+
+    ui_utils.wait_and_click(caller, text='Save', resource_id='android:id/title')
+
+    node = ui_utils.wait_and_get_xml_node(caller, timeout=30, text=TMO_APN['Name'])
+    bounds = node.parentNode.nextSibling.attributes['bounds'].value
+    caller.log.info('Bounds: %s', bounds)
+    ui_utils.wait_and_click(caller,
+                            text='',
+                            resource_id='com.android.settings:id/apn_radiobutton',
+                            bounds= bounds)
+    time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+
+    if msg_type is not None:
+        message_test(
+            log,
+            caller,
+            callee,
+            mo_rat=mo_rat,
+            mt_rat='general',
+            msg_type=msg_type)
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 6fd9e90..8f711d9 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_PORT_INDEX
 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
@@ -182,17 +183,59 @@
 
 
 def get_subid_from_slot_index(log, ad, sim_slot_index):
-    """ Get the subscription ID for a SIM at a particular slot
+    """ Get the subscription ID for a SIM at a particular slot.
 
     Args:
         ad: android_device object.
+        sim_slot_index: 0 for pSIM,
+                        1 for eSIM port 0,
+                        2 for eSIM port 1.
 
     Returns:
         result: Subscription ID
     """
+    siminfo = ad.adb.shell(
+        "content query --uri content://telephony/siminfo")
+    if "port_index" in siminfo and getattr(ad, "mep", False):
+        pattern_port = re.compile(r"port_index=(\d)")
+        pattern_sub = re.compile(r" _id=(\d+)")
+        pattern_embedded = re.compile(r"is_embedded=(\d+)")
+        siminfo_list = siminfo.splitlines()
+        for row in siminfo_list:
+            sub_id = pattern_sub.findall(row)
+            sub_id = int(sub_id[-1]) if sub_id else INVALID_SUB_ID
+            port_id = pattern_port.findall(row)
+            port_id = int(port_id[-1]) if port_id else INVALID_PORT_INDEX
+            is_embedded = int(pattern_embedded.findall(row)[-1])
+            if port_id == INVALID_PORT_INDEX:
+                continue
+            elif sim_slot_index == 0 and is_embedded == 0:
+                return sub_id
+            elif sim_slot_index-1 == port_id and is_embedded == 1:
+                return sub_id
+    else:
+        sim_slot_index = 1 if sim_slot_index else 0
+        subInfo = ad.droid.subscriptionGetAllSubInfoList()
+        for info in subInfo:
+            if info['simSlotIndex'] == sim_slot_index:
+                return info['subscriptionId']
+    return INVALID_SUB_ID
+
+
+def get_subid_from_logical_slot(ad, logical_slot):
+    """ Get the subscription ID for a SIM at a particular logical slot.
+
+    Args:
+        ad: android_device object.
+        logical_slot: The logical slot(0 or 1).
+
+    Returns:
+        result: Subscription ID
+    """
+    logical_slot = 1 if logical_slot else 0
     subInfo = ad.droid.subscriptionGetAllSubInfoList()
     for info in subInfo:
-        if info['simSlotIndex'] == sim_slot_index:
+        if info['simSlotIndex'] == logical_slot:
             return info['subscriptionId']
     return INVALID_SUB_ID
 
@@ -395,13 +438,12 @@
         ad.log.warning("Invalid sub ID at slot 0")
         return False
     operator = get_operatorname_from_slot_index(ad, 0)
-    if get_default_data_sub_id(ad) == sub_id:
-        ad.log.info("Current DDS is already on %s", operator)
+    if ad.droid.subscriptionGetDefaultDataSubId() == sub_id:
+        ad.log.info("Current DDS is already on Sub %s(%s)", sub_id, operator)
         return True
-    ad.log.info("Setting DDS on %s", operator)
+    ad.log.info("Setting DDS on Sub %s(%s)", sub_id, operator)
     set_subid_for_data(ad, sub_id)
     ad.droid.telephonyToggleDataConnection(True)
-    time.sleep(WAIT_TIME_CHANGE_DATA_SUB_ID)
     if get_default_data_sub_id(ad) == sub_id:
         return True
     else:
@@ -414,13 +456,12 @@
         ad.log.warning("Invalid sub ID at slot 1")
         return False
     operator = get_operatorname_from_slot_index(ad, 1)
-    if get_default_data_sub_id(ad) == sub_id:
-        ad.log.info("Current DDS is already on %s", operator)
+    if ad.droid.subscriptionGetDefaultDataSubId() == sub_id:
+        ad.log.info("Current DDS is already on Sub %s(%s)", sub_id, operator)
         return True
-    ad.log.info("Setting DDS on %s", operator)
+    ad.log.info("Setting DDS on Sub %s(%s)", sub_id, operator)
     set_subid_for_data(ad, sub_id)
     ad.droid.telephonyToggleDataConnection(True)
-    time.sleep(WAIT_TIME_CHANGE_DATA_SUB_ID)
     if get_default_data_sub_id(ad) == sub_id:
         return True
     else:
@@ -433,6 +474,9 @@
     Args:
         ad: android device object.
         dds_slot: the slot which be set to DDS.
+                  0 for pSIM,
+                  1 for eSIM port 0,
+                  2 for eSIM port 1.
 
     Returns:
         True if success, False if fail.
@@ -442,13 +486,12 @@
         ad.log.warning("Invalid sub ID at slot %d", dds_slot)
         return False
     operator = get_operatorname_from_slot_index(ad, dds_slot)
-    if get_default_data_sub_id(ad) == sub_id:
-        ad.log.info("Current DDS is already on %s", operator)
+    if ad.droid.subscriptionGetDefaultDataSubId() == sub_id:
+        ad.log.info("Current DDS is already on Sub %s(%s)", sub_id, operator)
         return True
-    ad.log.info("Setting DDS on %s", operator)
+    ad.log.info("Setting DDS on Sub %s(%s)", sub_id, operator)
     set_subid_for_data(ad, sub_id)
     ad.droid.telephonyToggleDataConnection(True)
-    time.sleep(WAIT_TIME_CHANGE_DATA_SUB_ID)
     if get_default_data_sub_id(ad) == sub_id:
         return True
     else:
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 3d7e935..2feb452 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
@@ -14,6 +14,7 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+from typing import Sequence
 from future import standard_library
 standard_library.install_aliases()
 
@@ -27,6 +28,7 @@
 import struct
 
 from acts import signals
+from acts.controllers.android_device import AndroidDevice
 from queue import Empty
 from acts.asserts import abort_all
 from acts.controllers.adb_lib.error import AdbCommandError, AdbError
@@ -34,6 +36,7 @@
 from acts.controllers.android_device import list_fastboot_devices
 
 from acts.libs.proc.job import TimeoutError
+from acts_contrib.test_utils.net import ui_utils
 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 AOSP_PREFIX
@@ -65,6 +68,7 @@
 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 SimSlotInfo
 from acts_contrib.test_utils.tel.tel_defines import RAT_UNKNOWN
 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
@@ -107,6 +111,7 @@
 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_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_logical_slot
 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
@@ -209,12 +214,12 @@
     setattr(ad, 'telephony', device_props)
 
 
-def setup_droid_properties(log, ad, sim_filename=None):
+def setup_droid_properties(log, ad, sim_filename=None, all_sub=False):
 
     if ad.skip_sl4a:
         return setup_droid_properties_by_adb(
             log, ad, sim_filename=sim_filename)
-    refresh_droid_config(log, ad)
+    refresh_droid_config(log, ad, all_sub)
     sim_data = {}
     if sim_filename:
         try:
@@ -278,12 +283,13 @@
     ad.log.debug("telephony = %s", ad.telephony)
 
 
-def refresh_droid_config(log, ad):
+def refresh_droid_config(log, ad, all_sub = False):
     """ Update Android Device telephony records for each sub_id.
 
     Args:
         log: log object
         ad: android device object
+        all_sub: True to record all sub id(include inactive SIM.)
 
     Returns:
         None
@@ -307,7 +313,7 @@
         else:
             isopportunistic = -1
 
-        if sim_slot != INVALID_SIM_SLOT_INDEX:
+        if sim_slot != INVALID_SIM_SLOT_INDEX or all_sub:
             if sub_id not in ad.telephony["subscription"]:
                 ad.telephony["subscription"][sub_id] = {}
             sub_record = ad.telephony["subscription"][sub_id]
@@ -478,7 +484,8 @@
     ad.log.info("Change airplane mode from %s to %s", cur_state, new_state)
     try:
         ad.adb.shell("settings put global airplane_mode_on %s" % int(new_state))
-        ad.adb.shell("am broadcast -a android.intent.action.AIRPLANE_MODE")
+        ad.adb.shell("am broadcast -a android.intent.action.AIRPLANE_MODE "
+                     "--ez state %s" % new_state)
     except Exception as e:
         ad.log.error(e)
         return False
@@ -1068,12 +1075,12 @@
     else:
         phone_count = ad.droid.telephonyGetPhoneCount()
 
-    slot_0_subid = get_subid_from_slot_index(ad.log, ad, 0)
+    slot_0_subid = get_subid_from_logical_slot(ad, 0)
     if slot_0_subid != INVALID_SUB_ID:
         configs[slot_0_subid] = {}
 
     if phone_count == 2:
-        slot_1_subid = get_subid_from_slot_index(ad.log, ad, 1)
+        slot_1_subid = get_subid_from_logical_slot(ad, 1)
         if slot_1_subid != INVALID_SUB_ID:
             configs[slot_1_subid] = {}
 
@@ -2771,7 +2778,7 @@
         build_id_override(ad, build_id)
 
 
-def enable_privacy_usage_diagnostics(ad):
+def check_and_enable_privacy_usage_diagnostics(ad):
     try:
         ad.ensure_screen_on()
         ad.send_keycode('HOME')
@@ -2779,9 +2786,25 @@
         cmd = ('am start -n com.google.android.gms/com.google.android.gms.'
                'usagereporting.settings.UsageReportingActivity')
         ad.adb.shell(cmd)
-    # perform the toggle
-        ad.send_keycode('TAB')
-        ad.send_keycode('ENTER')
+    # perform the toggle using UI
+        resource = {
+        'resource_id': 'android:id/switch_widget',
+        }
+        node = ui_utils.wait_and_get_xml_node(ad,
+                                        timeout=30,
+                                        sibling=resource,
+                                        text="Usage & diagnostics",
+                                        resource_id="com.google.android.gms:id/switch_text")
+        current_state = node.attributes['checked'].value
+
+        if current_state == "false":
+            ad.log.info("Enabling Usage & diagnostics")
+            ui_utils.wait_and_click(ad,
+                                    text="Usage & diagnostics",
+                                    resource_id="com.google.android.gms:id/switch_text")
+        else:
+            ad.log.info("Usage & diagnostics is already enabled")
+
     except Exception:
         ad.log.info("Unable to toggle Usage and Diagnostics")
 
@@ -2797,6 +2820,8 @@
     existing_build_id = ad.adb.getprop("ro.build.id")
     if postfix is not None and postfix in build_id:
         ad.log.info("Build id already contains %s", postfix)
+        if postfix == 'STATIONARY_TEST':
+            check_and_enable_privacy_usage_diagnostics(ad)
         return
     if not new_build_id:
         if postfix and build_id:
@@ -2805,7 +2830,7 @@
         return
     ad.log.info("Override build id %s with %s", existing_build_id,
                 new_build_id)
-    enable_privacy_usage_diagnostics(ad)
+    check_and_enable_privacy_usage_diagnostics(ad)
     adb_disable_verity(ad)
     ad.adb.remount()
     if "backup.prop" not in ad.adb.shell("ls /sdcard/"):
@@ -2992,6 +3017,50 @@
     return False
 
 
+def change_slot(ad: AndroidDevice, sim_slot: Sequence[SimSlotInfo],
+                timeout: int = MAX_WAIT_TIME_FOR_STATE_CHANGE) -> bool:
+    """Enable Sims for Slot Mapping Mode.
+
+    Args:
+        ad: android device object.
+        sim_slot: a list which contains 2 slots for logical slot 0 and 1.
+            e.g. [SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1]
+        timeout: wait time for state change.
+
+    Returns:
+        True if success, False otherwise.
+    """
+    if not getattr(ad, "mep", False): return
+
+    port_id = [sim_slot[0].value[1], sim_slot[1].value[1]]
+    phy_slot_id = [sim_slot[0].value[2], sim_slot[1].value[2]]
+    ad.adb.shell(
+        "am broadcast -a android.telephony.euicc.action.TEST_PROFILE "
+        "-n com.google.android.euicc/com.android.euicc.receiver."
+        "ProfileTestReceiver --es 'operation' 'changeSlot' --es "
+        "'simSlotMapping' \"[{'port':%d,'physical':%d,'logical':0},{'port':%d,"
+        "'physical':%d,'logical':1}]\"" % (port_id[0], phy_slot_id[0],
+        port_id[1], phy_slot_id[1]))
+    time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+
+    while timeout > 0:
+        sim_state = ad.adb.getprop("gsm.sim.state").split(",")
+        if (get_subid_from_slot_index(
+            ad.log, ad, sim_slot[0].value[0]) != INVALID_SUB_ID
+            ) and (get_subid_from_slot_index(
+                ad.log, ad, sim_slot[1].value[0]) != INVALID_SUB_ID):
+            if (set(sim_state) - {
+                SIM_STATE_UNKNOWN, SIM_STATE_ABSENT, SIM_STATE_NOT_READY}):
+                get_phone_capability(ad)
+                return True
+        timeout = timeout - WAIT_TIME_BETWEEN_STATE_CHECK
+        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+
+    ad.log.warning("Fail to change SIM slot %s, sim_state=%s",
+        sim_slot, sim_state)
+    return False
+
+
 def power_on_sim_by_adb(ad, sim_slot_id,
                          timeout=MAX_WAIT_TIME_FOR_STATE_CHANGE):
     """Enable pSIM/eSIM SUB by adb command.
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 2df234c..09722c4 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
@@ -129,8 +129,13 @@
         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":
+    elif (network_type == "LTE_WCDMA" or network_type == "LTE_EDGE" or
+        network_type == "LTE_GSM"):
         voice_call_type_dict = update_voice_call_type_dict(dut, "CSFB")
+    elif network_type == "EDGE_EDGE":
+        voice_call_type_dict = update_voice_call_type_dict(dut, "EDGE")
+    elif network_type == "GSM_GSM":
+        voice_call_type_dict = update_voice_call_type_dict(dut, "GSM")
     else:
         voice_call_type_dict = update_voice_call_type_dict(dut, "UNKNOWN")
     return voice_call_type_dict
@@ -144,11 +149,11 @@
     Return:
         voice_call_type: Voice call status
     """
-    if dut in voice_call_type.keys():
-        voice_call_type[dut][key] += 1
-    else:
+    if dut not in voice_call_type.keys():
         voice_call_type[dut] = {key:0}
-        voice_call_type[dut][key] += 1
+    if key not in voice_call_type[dut].keys():
+        voice_call_type[dut].update({key:0})
+    voice_call_type[dut][key] += 1
     return voice_call_type
 
 
diff --git a/acts_tests/acts_contrib/test_utils/wifi/WifiBaseTest.py b/acts_tests/acts_contrib/test_utils/wifi/WifiBaseTest.py
index 1f80858..f57fab9 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/WifiBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/WifiBaseTest.py
@@ -18,19 +18,14 @@
 """
 
 import copy
-import itertools
 import os
 import time
 
-import acts.controllers.access_point as ap
-
 from acts import asserts
+from acts import context
 from acts import signals
 from acts import utils
 from acts.base_test import BaseTestClass
-from acts.signals import TestSignal
-from acts.controllers import android_device
-from acts.controllers.access_point import AccessPoint
 from acts.controllers.ap_lib import hostapd_ap_preset
 from acts.controllers.ap_lib import hostapd_bss_settings
 from acts.controllers.ap_lib import hostapd_constants
@@ -39,6 +34,9 @@
 from acts_contrib.test_utils.net import net_test_utils as nutils
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 
+from mobly.base_test import STAGE_NAME_TEARDOWN_CLASS
+
+WifiEnums = wutils.WifiEnums
 AP_1 = 0
 AP_2 = 1
 MAX_AP_COUNT = 2
@@ -50,20 +48,15 @@
         self.enable_packet_log = False
         self.packet_log_2g = hostapd_constants.AP_DEFAULT_CHANNEL_2G
         self.packet_log_5g = hostapd_constants.AP_DEFAULT_CHANNEL_5G
+        self.tcpdump_proc = []
+        self.packet_log_pid = {}
 
     def setup_class(self):
         if hasattr(self, 'attenuators') and self.attenuators:
             for attenuator in self.attenuators:
                 attenuator.set_atten(0)
-        opt_param = ["pixel_models", "cnss_diag_file", "country_code_file"]
+        opt_param = ["country_code_file"]
         self.unpack_userparams(opt_param_names=opt_param)
-        if hasattr(self, "cnss_diag_file"):
-            if isinstance(self.cnss_diag_file, list):
-                self.cnss_diag_file = self.cnss_diag_file[0]
-            if not os.path.isfile(self.cnss_diag_file):
-                self.cnss_diag_file = os.path.join(
-                    self.user_params[Config.key_config_path.value],
-                    self.cnss_diag_file)
         if self.enable_packet_log and hasattr(self, "packet_capture"):
             self.packet_logger = self.packet_capture[0]
             self.packet_logger.configure_monitor_mode("2G", self.packet_log_2g)
@@ -80,14 +73,13 @@
                             self.country_code_file)
                     self.country_code = utils.load_config(
                         self.country_code_file)["country"]
-                    wutils.set_wifi_country_code(ad, self.country_code)
+                else:
+                    self.country_code = WifiEnums.CountryCode.US
+                wutils.set_wifi_country_code(ad, self.country_code)
 
     def setup_test(self):
-        if (hasattr(self, "android_devices")
-                and hasattr(self, "cnss_diag_file")
-                and hasattr(self, "pixel_models")):
-            wutils.start_cnss_diags(self.android_devices, self.cnss_diag_file,
-                                    self.pixel_models)
+        if (hasattr(self, "android_devices")):
+            wutils.start_all_wlan_logs(self.android_devices)
         self.tcpdump_proc = []
         if hasattr(self, "android_devices"):
             for ad in self.android_devices:
@@ -98,10 +90,8 @@
                                                     self.test_name)
 
     def teardown_test(self):
-        if (hasattr(self, "android_devices")
-                and hasattr(self, "cnss_diag_file")
-                and hasattr(self, "pixel_models")):
-            wutils.stop_cnss_diags(self.android_devices, self.pixel_models)
+        if (hasattr(self, "android_devices")):
+            wutils.stop_all_wlan_logs(self.android_devices)
             for proc in self.tcpdump_proc:
                 nutils.stop_tcpdump(proc[0],
                                     proc[1],
@@ -114,17 +104,21 @@
                              test_status=True)
             self.packet_log_pid = {}
 
+    def teardown_class(self):
+        begin_time = utils.get_current_epoch_time()
+        super().teardown_class()
+        for device in getattr(self, "fuchsia_devices", []):
+            device.take_bug_report(STAGE_NAME_TEARDOWN_CLASS, begin_time)
+
     def on_fail(self, test_name, begin_time):
         if hasattr(self, "android_devices"):
             for ad in self.android_devices:
                 ad.take_bug_report(test_name, begin_time)
                 ad.cat_adb_log(test_name, begin_time)
                 wutils.get_ssrdumps(ad)
-            if (hasattr(self, "cnss_diag_file")
-                    and hasattr(self, "pixel_models")):
-                wutils.stop_cnss_diags(self.android_devices, self.pixel_models)
-                for ad in self.android_devices:
-                    wutils.get_cnss_diag_log(ad)
+            wutils.stop_all_wlan_logs(self.android_devices)
+            for ad in self.android_devices:
+                wutils.get_wlan_logs(ad)
             for proc in self.tcpdump_proc:
                 nutils.stop_tcpdump(proc[0], proc[1], self.test_name)
             self.tcpdump_proc = []
@@ -134,6 +128,53 @@
                              test_status=False)
             self.packet_log_pid = {}
 
+        # Gets a wlan_device log and calls the generic device fail on DUT.
+        for device in getattr(self, "fuchsia_devices", []):
+            self.on_device_fail(device, test_name, begin_time)
+
+    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 device has the
+        'take_bug_report_on_fail' config value, and 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 hasattr(device,
+                   "hard_reboot_on_fail") and 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 = self.access_point.get_dhcp_logs()
+        if dhcp_log:
+            dhcp_log_file = open(dhcp_full_out_path, 'w')
+            dhcp_log_file.write(dhcp_log)
+            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()
+
     def get_psk_network(
             self,
             mirror_ap,
@@ -467,10 +508,10 @@
         for i in range(ap_count):
             network_list = []
             if wpa1_network:
-                wpa1_dict = self.get_psk_network(mirror_ap,
-                                                 self.wpa1_networks,
+                wpa1_dict = self.get_psk_network(mirror_ap, self.wpa1_networks,
                                                  hidden, same_ssid,
-                                                 ssid_length_2g, ssid_length_5g,
+                                                 ssid_length_2g,
+                                                 ssid_length_5g,
                                                  passphrase_length_2g,
                                                  passphrase_length_5g)
                 wpa1_dict[hostapd_constants.BAND_2G]["security"] = "psk"
@@ -543,16 +584,18 @@
                 sae_dict[hostapd_constants.BAND_5G]["security"] = "sae"
                 network_list.append(sae_dict)
             if saemixed_network:
-                saemixed_dict = self.get_psk_network(mirror_ap, self.saemixed_networks,
-                                                hidden, same_ssid,
-                                                hostapd_constants.SAE_KEY_MGMT,
-                                                ssid_length_2g, ssid_length_5g,
-                                                passphrase_length_2g,
-                                                passphrase_length_5g)
-                saemixed_dict[hostapd_constants.BAND_2G]["security"] = "sae-mixed"
-                saemixed_dict[hostapd_constants.BAND_5G]["security"] = "sae-mixed"
-                saemixed_dict[hostapd_constants.BAND_2G]["ieee80211w"] = ieee80211w
-                saemixed_dict[hostapd_constants.BAND_5G]["ieee80211w"] = ieee80211w
+                saemixed_dict = self.get_psk_network(
+                    mirror_ap, self.saemixed_networks, hidden, same_ssid,
+                    hostapd_constants.SAE_KEY_MGMT, ssid_length_2g,
+                    ssid_length_5g, passphrase_length_2g, passphrase_length_5g)
+                saemixed_dict[
+                    hostapd_constants.BAND_2G]["security"] = "sae-mixed"
+                saemixed_dict[
+                    hostapd_constants.BAND_5G]["security"] = "sae-mixed"
+                saemixed_dict[
+                    hostapd_constants.BAND_2G]["ieee80211w"] = ieee80211w
+                saemixed_dict[
+                    hostapd_constants.BAND_5G]["ieee80211w"] = ieee80211w
                 network_list.append(saemixed_dict)
             self.access_points[i].configure_ap(network_list, channels_2g[i],
                                                channels_5g[i])
@@ -561,8 +604,8 @@
                 self.access_points[i].get_bssids_for_wifi_networks())
             if mirror_ap:
                 self.access_points[i + 1].configure_ap(network_list,
-                                                       channels_2g[i+1],
-                                                       channels_5g[i+1])
+                                                       channels_2g[i + 1],
+                                                       channels_5g[i + 1])
                 self.access_points[i + 1].start_ap()
                 self.bssid_map.append(
                     self.access_points[i + 1].get_bssids_for_wifi_networks())
diff --git a/acts_tests/acts_contrib/test_utils/wifi/aware/AwareBaseTest.py b/acts_tests/acts_contrib/test_utils/wifi/aware/AwareBaseTest.py
index 14c5c0e..4a51ff7 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/aware/AwareBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/aware/AwareBaseTest.py
@@ -36,24 +36,15 @@
     device_startup_offset = 2
 
     def setup_class(self):
-        opt_param = ["pixel_models", "cnss_diag_file", "ranging_role_concurrency_flexible_models"]
+        opt_param = ["ranging_role_concurrency_flexible_models"]
         self.unpack_userparams(opt_param_names=opt_param)
-        if hasattr(self, "cnss_diag_file"):
-            if isinstance(self.cnss_diag_file, list):
-                self.cnss_diag_file = self.cnss_diag_file[0]
-            if not os.path.isfile(self.cnss_diag_file):
-                self.cnss_diag_file = os.path.join(
-                    self.user_params[Config.key_config_path.value],
-                    self.cnss_diag_file)
 
     def setup_test(self):
         required_params = ("aware_default_power_mode",
                            "dbs_supported_models",)
         self.unpack_userparams(required_params)
 
-        if hasattr(self, "cnss_diag_file") and hasattr(self, "pixel_models"):
-            wutils.start_cnss_diags(
-                self.android_devices, self.cnss_diag_file, self.pixel_models)
+        wutils.start_all_wlan_logs(self.android_devices)
         self.tcpdump_proc = []
         if hasattr(self, "android_devices"):
             for ad in self.android_devices:
@@ -86,8 +77,7 @@
             ad.ed.clear_all_events()
 
     def teardown_test(self):
-        if hasattr(self, "cnss_diag_file") and hasattr(self, "pixel_models"):
-            wutils.stop_cnss_diags(self.android_devices, self.pixel_models)
+        wutils.stop_all_wlan_logs(self.android_devices)
         for proc in self.tcpdump_proc:
             nutils.stop_tcpdump(
                     proc[0], proc[1], self.test_name, pull_dump=False)
@@ -144,10 +134,9 @@
             ad.take_bug_report(test_name, begin_time)
             ad.cat_adb_log(test_name, begin_time)
             wutils.get_ssrdumps(ad)
-        if hasattr(self, "cnss_diag_file") and hasattr(self, "pixel_models"):
-            wutils.stop_cnss_diags(self.android_devices, self.pixel_models)
-            for ad in self.android_devices:
-                wutils.get_cnss_diag_log(ad)
+        wutils.stop_all_wlan_logs(self.android_devices)
+        for ad in self.android_devices:
+            wutils.get_wlan_logs(ad)
         for proc in self.tcpdump_proc:
             nutils.stop_tcpdump(proc[0], proc[1], self.test_name)
         self.tcpdump_proc = []
diff --git a/acts_tests/acts_contrib/test_utils/wifi/aware/aware_const.py b/acts_tests/acts_contrib/test_utils/wifi/aware/aware_const.py
index f0f385b..3226263 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/aware/aware_const.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/aware/aware_const.py
@@ -61,6 +61,7 @@
 DISCOVERY_KEY_RANGING_ENABLED = "RangingEnabled"
 DISCOVERY_KEY_MIN_DISTANCE_MM = "MinDistanceMm"
 DISCOVERY_KEY_MAX_DISTANCE_MM = "MaxDistanceMm"
+DISCOVERY_KEY_INSTANT_COMMUNICATION_MODE = "InstantModeEnabled"
 
 PUBLISH_TYPE_UNSOLICITED = 0
 PUBLISH_TYPE_SOLICITED = 1
@@ -150,6 +151,7 @@
 CAP_MAX_QUEUED_TRANSMIT_MESSAGES = "maxQueuedTransmitMessages"
 CAP_MAX_SUBSCRIBE_INTERFACE_ADDRESSES = "maxSubscribeInterfaceAddresses"
 CAP_SUPPORTED_CIPHER_SUITES = "supportedCipherSuites"
+CAP_SUPPORTED_INSTANT_COMMUNICATION_MODE = "isInstantCommunicationModeSupported"
 
 ######################################################
 # WifiAwareNetworkCapabilities keys
diff --git a/acts_tests/acts_contrib/test_utils/wifi/aware/aware_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/aware/aware_test_utils.py
index 4f22289..06b2e04 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/aware/aware_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/aware/aware_test_utils.py
@@ -714,7 +714,8 @@
                             match_filter=None,
                             match_filter_list=None,
                             ttl=0,
-                            term_cb_enable=True):
+                            term_cb_enable=True,
+                            instant_mode=None):
     """Create a publish discovery configuration based on input parameters.
 
   Args:
@@ -726,6 +727,7 @@
     ttl: Time-to-live - defaults to 0 (i.e. non-self terminating)
     term_cb_enable: True (default) to enable callback on termination, False
                     means that no callback is called when session terminates.
+    instant_mode: set the band to use instant communication mode, 2G or 5G
   Returns:
     publish discovery configuration object.
   """
@@ -738,6 +740,8 @@
         config[aconsts.DISCOVERY_KEY_MATCH_FILTER] = match_filter
     if match_filter_list is not None:
         config[aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST] = match_filter_list
+    if instant_mode is not None:
+        config[aconsts.DISCOVERY_KEY_INSTANT_COMMUNICATION_MODE] = instant_mode
     config[aconsts.DISCOVERY_KEY_TTL] = ttl
     config[aconsts.DISCOVERY_KEY_TERM_CB_ENABLED] = term_cb_enable
     return config
diff --git a/acts_tests/acts_contrib/test_utils/wifi/ota_chamber.py b/acts_tests/acts_contrib/test_utils/wifi/ota_chamber.py
index d74e785..280e72e 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/ota_chamber.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/ota_chamber.py
@@ -16,6 +16,7 @@
 
 import contextlib
 import io
+import requests
 import serial
 import time
 from acts import logger
@@ -52,6 +53,7 @@
     Base class provides functions whose implementation is shared by all
     chambers.
     """
+
     def reset_chamber(self):
         """Resets the chamber to its zero/home state."""
         raise NotImplementedError
@@ -83,6 +85,7 @@
 
 class MockChamber(OtaChamber):
     """Class that implements mock chamber for test development and debug."""
+
     def __init__(self, config):
         self.config = config.copy()
         self.device_id = self.config['device_id']
@@ -120,6 +123,7 @@
 
 class OctoboxChamber(OtaChamber):
     """Class that implements Octobox chamber."""
+
     def __init__(self, config):
         self.config = config.copy()
         self.device_id = self.config['device_id']
@@ -129,7 +133,9 @@
         utils.exe_cmd('sudo {} -d {} -i 0'.format(self.TURNTABLE_FILE_PATH,
                                                   self.device_id))
         self.current_mode = None
-        self.SUPPORTED_BANDS = ['2.4GHz', 'UNII-1', 'UNII-2', 'UNII-3', '6GHz']
+        self.SUPPORTED_BANDS = [
+            '2.4GHz', 'UNII-1', 'UNII-2', 'UNII-3', 'UNII-4', '6GHz'
+        ]
 
     def set_orientation(self, orientation):
         self.log.info('Setting orientation to {} degrees.'.format(orientation))
@@ -142,12 +148,49 @@
         self.set_orientation(0)
 
 
+class OctoboxChamberV2(OtaChamber):
+    """Class that implements Octobox chamber."""
+
+    def __init__(self, config):
+        self.config = config.copy()
+        self.address = config['ip_address']
+        self.data = requests.get("http://{}/api/turntable".format(
+            self.address))
+        self.vel_target = '10000'
+        self.current_mode = None
+        self.SUPPORTED_BANDS = [
+            '2.4GHz', 'UNII-1', 'UNII-2', 'UNII-3', 'UNII-4', '6GHz'
+        ]
+        self.log = logger.create_tagged_trace_logger('OtaChamber|{}'.format(
+            self.address))
+
+    def set_orientation(self, orientation):
+        self.log.info('Setting orientation to {} degrees.'.format(orientation))
+        if orientation > 720:
+            raise ValueError('Orientation may not exceed 720.')
+        set_position_submission = {
+            "action": "pos",
+            "enable": "1",
+            "pos_target": orientation,
+            "vel_target": self.vel_target
+        }
+        result = requests.post("http://{}/api/turntable".format(self.address),
+                               json=set_position_submission)
+        self.log.debug(result)
+
+    def reset_chamber(self):
+        self.log.info('Resetting chamber to home state')
+        self.set_orientation(0)
+
+
 class ChamberAutoConnect(object):
+
     def __init__(self, chamber, chamber_config):
         self._chamber = chamber
         self._config = chamber_config
 
     def __getattr__(self, item):
+
         def chamber_call(*args, **kwargs):
             self._chamber.connect(self._config['ip_address'],
                                   self._config['username'],
@@ -159,6 +202,7 @@
 
 class BluetestChamber(OtaChamber):
     """Class that implements Octobox chamber."""
+
     def __init__(self, config):
         import flow
         self.config = config.copy()
@@ -167,7 +211,9 @@
         self.chamber = ChamberAutoConnect(flow.Flow(), self.config)
         self.stirrer_ids = [0, 1, 2]
         self.current_mode = None
-        self.SUPPORTED_BANDS = ['2.4GHz', 'UNII-1', 'UNII-2', 'UNII-3']
+        self.SUPPORTED_BANDS = [
+            '2.4GHz', 'UNII-1', 'UNII-2', 'UNII-3', 'UNII-4', '6GHz'
+        ]
 
     # Capture print output decorator
     @staticmethod
@@ -248,6 +294,7 @@
 
 class EInstrumentChamber(OtaChamber):
     """Class that implements Einstrument Chamber."""
+
     def __init__(self, config):
         self.config = config.copy()
         self.device_id = self.config['device_id']
diff --git a/acts_tests/acts_contrib/test_utils/wifi/ota_sniffer.py b/acts_tests/acts_contrib/test_utils/wifi/ota_sniffer.py
index 395fed2..7cc52da 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/ota_sniffer.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/ota_sniffer.py
@@ -18,6 +18,7 @@
 import os
 import posixpath
 import time
+import zipfile
 import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
 
 from acts import context
@@ -126,6 +127,7 @@
 
 class MockSniffer(OtaSnifferBase):
     """Class that implements mock sniffer for test development and debug."""
+
     def __init__(self, config):
         self.log = logger.create_tagged_trace_logger('Mock Sniffer')
 
@@ -443,6 +445,11 @@
 
         if self.sniffer_output_file_type == 'csv':
             log_file = self._process_tshark_dump(log_file)
+        if self.sniffer_output_file_type == 'pcap':
+            zip_file_path = log_file[:-4] + "zip"
+            zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED).write(
+                log_file, arcname=log_file.split('/')[-1])
+            os.remove(log_file)
 
         self.sniffer_proc_pid = None
         return log_file
@@ -450,6 +457,7 @@
 
 class TsharkSnifferOnUnix(TsharkSnifferBase):
     """Class that implements Tshark based sniffer controller on Unix systems."""
+
     def _scan_for_networks(self):
         """Scans the wireless networks on the sniffer.
 
@@ -480,6 +488,7 @@
 
 class TsharkSnifferOnLinux(TsharkSnifferBase):
     """Class that implements Tshark based sniffer controller on Linux."""
+
     def __init__(self, config):
         super().__init__(config)
         self._init_sniffer()
diff --git a/acts_tests/acts_contrib/test_utils/wifi/p2p/WifiP2pBaseTest.py b/acts_tests/acts_contrib/test_utils/wifi/p2p/WifiP2pBaseTest.py
index c0b18ae..ebc1e6b 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/p2p/WifiP2pBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/p2p/WifiP2pBaseTest.py
@@ -40,7 +40,7 @@
             ad.droid.wakeLockAcquireBright()
             ad.droid.wakeUpNow()
         required_params = ()
-        optional_params = ("skip_read_factory_mac", "pixel_models", "cnss_diag_file")
+        optional_params = ("skip_read_factory_mac")
         self.unpack_userparams(required_params,
                                optional_params,
                                skip_read_factory_mac=0)
@@ -87,13 +87,6 @@
                 "DUT3's p2p should be initialized but it didn't")
             self.dut3.name = "Android_" + self.dut3.serial
             self.dut3.droid.wifiP2pSetDeviceName(self.dut3.name)
-        if hasattr(self, "cnss_diag_file"):
-            if isinstance(self.cnss_diag_file, list):
-                self.cnss_diag_file = self.cnss_diag_file[0]
-            if not os.path.isfile(self.cnss_diag_file):
-                self.cnss_diag_file = os.path.join(
-                    self.user_params[Config.key_config_path.value],
-                    self.cnss_diag_file)
 
     def teardown_class(self):
         self.dut1.droid.wifiP2pClose()
@@ -109,9 +102,7 @@
             ad.droid.goToSleepNow()
 
     def setup_test(self):
-        if hasattr(self, "cnss_diag_file") and hasattr(self, "pixel_models"):
-            wutils.start_cnss_diags(
-                self.android_devices, self.cnss_diag_file, self.pixel_models)
+        wutils.start_all_wlan_logs(self.android_devices)
         self.tcpdump_proc = []
         if hasattr(self, "android_devices"):
             for ad in self.android_devices:
@@ -122,8 +113,7 @@
             ad.ed.clear_all_events()
 
     def teardown_test(self):
-        if hasattr(self, "cnss_diag_file") and hasattr(self, "pixel_models"):
-            wutils.stop_cnss_diags(self.android_devices, self.pixel_models)
+        wutils.stop_all_wlan_logs(self.android_devices)
         for proc in self.tcpdump_proc:
             nutils.stop_tcpdump(
                     proc[0], proc[1], self.test_name, pull_dump=False)
@@ -143,10 +133,9 @@
             ad.take_bug_report(test_name, begin_time)
             ad.cat_adb_log(test_name, begin_time)
             wutils.get_ssrdumps(ad)
-        if hasattr(self, "cnss_diag_file") and hasattr(self, "pixel_models"):
-            wutils.stop_cnss_diags(self.android_devices, self.pixel_models)
-            for ad in self.android_devices:
-                wutils.get_cnss_diag_log(ad)
+        wutils.stop_all_wlan_logs(self.android_devices)
+        for ad in self.android_devices:
+            wutils.get_wlan_logs(ad)
         for proc in self.tcpdump_proc:
             nutils.stop_tcpdump(proc[0], proc[1], self.test_name)
         self.tcpdump_proc = []
diff --git a/acts_tests/acts_contrib/test_utils/wifi/p2p/wifi_p2p_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/p2p/wifi_p2p_test_utils.py
index a96b287..10f3ea0 100755
--- a/acts_tests/acts_contrib/test_utils/wifi/p2p/wifi_p2p_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/p2p/wifi_p2p_test_utils.py
@@ -14,12 +14,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import logging
-import os
 import time
 
-from queue import Empty
-
 from acts import asserts
 from acts import utils
 from acts_contrib.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts
@@ -135,6 +131,7 @@
          ad_group_info_event['data']['Interface']))
     return ad_group_info_event['data']
 
+
 def is_ongoing_peer_ready(peerConfig, waitForPin):
     """Check whether the peer config is ready
 
@@ -151,10 +148,11 @@
     if not waitForPin:
         return True
     if WifiP2PEnums.WpsInfo.WPS_PIN_KEY in peerConfig['data'][
-        WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY]:
+            WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY]:
         return True
     return False
 
+
 def wait_for_ongoing_peer_ready(ad, waitForPin, maxPollingCount):
     """wait for the ongoing peer data ready
 
@@ -166,8 +164,8 @@
         the ongoing peer config
     """
     ad_peerConfig = None
-    ad.log.debug("%s is waiting for the ongoing peer, max polling count %s"
-        % (ad.name, maxPollingCount))
+    ad.log.debug("%s is waiting for the ongoing peer, max polling count %s" %
+                 (ad.name, maxPollingCount))
     while maxPollingCount > 0:
         ad.droid.requestP2pPeerConfigure()
         ad_peerConfig = ad.ed.pop_event(
@@ -184,6 +182,7 @@
     ad.log.debug(ad_peerConfig['data'])
     return ad_peerConfig
 
+
 #trigger p2p connect to ad2 from ad1
 def p2p_connect(ad1,
                 ad2,
@@ -226,12 +225,12 @@
                      p2pconsts.DEFAULT_TIMEOUT)
     if not isReconnect:
         # ad1 is the initiator, it should be ready soon.
-        ad1_peerConfig = wait_for_ongoing_peer_ready(ad1,
-            wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY, 6)
+        ad1_peerConfig = wait_for_ongoing_peer_ready(
+            ad1, wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY, 6)
         # auto-join tries 10 times to find groups, and
         # one round takes 2 - 3 seconds.
-        ad2_peerConfig = wait_for_ongoing_peer_ready(ad2,
-            wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_KEYPAD, 31)
+        ad2_peerConfig = wait_for_ongoing_peer_ready(
+            ad2, wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_KEYPAD, 31)
         if wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY:
             asserts.assert_true(
                 WifiP2PEnums.WpsInfo.WPS_PIN_KEY in ad1_peerConfig['data'][
@@ -340,7 +339,6 @@
     while not p2p_find_result:
         ad2.droid.wifiP2pStopPeerDiscovery()
         ad1.droid.wifiP2pStopPeerDiscovery()
-        ad2.droid.wifiP2pDiscoverPeers()
         ad1.droid.wifiP2pDiscoverPeers()
         ad1_event = ad1.ed.pop_event(p2pconsts.PEER_AVAILABLE_EVENT,
                                      p2pconsts.P2P_FIND_TIMEOUT)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/rtt/RttBaseTest.py b/acts_tests/acts_contrib/test_utils/wifi/rtt/RttBaseTest.py
index c11f66a..b9abd49 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/rtt/RttBaseTest.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/rtt/RttBaseTest.py
@@ -29,15 +29,8 @@
 class RttBaseTest(BaseTestClass):
 
     def setup_class(self):
-        opt_param = ["pixel_models", "cnss_diag_file", "ranging_role_concurrency_flexible_models"]
+        opt_param = ["ranging_role_concurrency_flexible_models"]
         self.unpack_userparams(opt_param_names=opt_param)
-        if hasattr(self, "cnss_diag_file"):
-            if isinstance(self.cnss_diag_file, list):
-                self.cnss_diag_file = self.cnss_diag_file[0]
-            if not os.path.isfile(self.cnss_diag_file):
-                self.cnss_diag_file = os.path.join(
-                    self.user_params[Config.key_config_path.value],
-                    self.cnss_diag_file)
 
     def setup_test(self):
         required_params = ("lci_reference", "lcr_reference",
@@ -54,9 +47,7 @@
         self.rtt_max_margin_exceeded_rate_one_sided_rtt_percentage = 50
         self.rtt_min_expected_rssi_dbm = -100
 
-        if hasattr(self, "cnss_diag_file") and hasattr(self, "pixel_models"):
-            wutils.start_cnss_diags(
-                self.android_devices, self.cnss_diag_file, self.pixel_models)
+        wutils.start_all_wlan_logs(self.android_devices)
         self.tcpdump_proc = []
         if hasattr(self, "android_devices"):
             for ad in self.android_devices:
@@ -80,8 +71,7 @@
             ad.rtt_capabilities = rutils.get_rtt_capabilities(ad)
 
     def teardown_test(self):
-        if hasattr(self, "cnss_diag_file") and hasattr(self, "pixel_models"):
-            wutils.stop_cnss_diags(self.android_devices, self.pixel_models)
+        wutils.stop_all_wlan_logs(self.android_devices)
         for proc in self.tcpdump_proc:
             nutils.stop_tcpdump(
                     proc[0], proc[1], self.test_name, pull_dump=False)
@@ -98,10 +88,9 @@
             ad.take_bug_report(test_name, begin_time)
             ad.cat_adb_log(test_name, begin_time)
             wutils.get_ssrdumps(ad)
-        if hasattr(self, "cnss_diag_file") and hasattr(self, "pixel_models"):
-            wutils.stop_cnss_diags(self.android_devices, self.pixel_models)
-            for ad in self.android_devices:
-                wutils.get_cnss_diag_log(ad)
+        wutils.stop_all_wlan_logs(self.android_devices)
+        for ad in self.android_devices:
+            wutils.get_wlan_logs(ad)
         for proc in self.tcpdump_proc:
             nutils.stop_tcpdump(proc[0], proc[1], self.test_name)
         self.tcpdump_proc = []
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_constants.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_constants.py
index 6247643..3ff7599 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_constants.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_constants.py
@@ -93,11 +93,6 @@
 AP_AUX = "aux_AP"
 SSID = "SSID"
 
-# cnss_diag property related constants
-DEVICES_USING_LEGACY_PROP = ["sailfish", "marlin", "walleye", "taimen", "muskie"]
-CNSS_DIAG_PROP = "persist.vendor.sys.cnss.diag_txt"
-LEGACY_CNSS_DIAG_PROP = "persist.sys.cnss.diag_txt"
-
 # Delay before registering the match callback.
 NETWORK_REQUEST_CB_REGISTER_DELAY_SEC = 2
 
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_datastore_utils.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_datastore_utils.py
index a79ff77..f897050 100755
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_datastore_utils.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_datastore_utils.py
@@ -16,15 +16,12 @@
 
 import json
 import logging
-import pprint
 import requests
-import time
 
 from acts import asserts
 from acts import signals
 from acts import utils
 from acts_contrib.test_utils.wifi import wifi_constants
-
 """This file consists of all the helper methods needed to interact with the
    Datastore @ https://chaos-188802.appspot.com/ used for Android Interop
    testing.
@@ -43,6 +40,7 @@
 # HTTP content type. JSON encoded with UTF-8 character encoding.
 HTTP_HEADER = {'content-type': 'application/json'}
 
+
 def add_device(name, ap_label, lab_label):
     """Add a device(AP or Packet Capturer) in datastore.
 
@@ -57,14 +55,17 @@
     logging.debug("Request = %s" % request)
     response = requests.post(request,
                              headers=HTTP_HEADER,
-                             data=json.dumps({"hostname":name,
-                                              "ap_label":ap_label,
-                                              "lab_label":lab_label}))
+                             data=json.dumps({
+                                 "hostname": name,
+                                 "ap_label": ap_label,
+                                 "lab_label": lab_label
+                             }))
     if response.json()['result'] == 'success':
         logging.info("Added device %s to datastore" % name)
         return True
     return False
 
+
 def remove_device(name):
     """Delete a device(AP or Packet Capturer) in datastore.
 
@@ -77,13 +78,14 @@
     logging.debug("Request = %s" % request)
     response = requests.put(request,
                             headers=HTTP_HEADER,
-                            data=json.dumps({"hostname":name}))
+                            data=json.dumps({"hostname": name}))
     result_str = "%s deleted." % name
     if result_str in response.text:
         logging.info("Removed device %s from datastore" % name)
         return True
     return False
 
+
 def lock_device(name, admin):
     """Lock a device(AP or Packet Capturer) in datastore.
 
@@ -97,12 +99,16 @@
     logging.debug("Request = %s" % request)
     response = requests.put(request,
                             headers=HTTP_HEADER,
-                            data=json.dumps({"hostname":name, "locked_by":admin}))
+                            data=json.dumps({
+                                "hostname": name,
+                                "locked_by": admin
+                            }))
     if response.json()['result']:
         logging.info("Locked device %s in datastore" % name)
         return True
     return False
 
+
 def unlock_device(name):
     """Un-lock a device(AP or Packet Capturer) in datastore.
 
@@ -115,12 +121,13 @@
     logging.debug("Request = %s" % request)
     response = requests.put(request,
                             headers=HTTP_HEADER,
-                            data=json.dumps({"hostname":name}))
+                            data=json.dumps({"hostname": name}))
     if response.json()['result']:
         logging.info("Finished un-locking AP %s in datastore" % name)
         return True
     return False
 
+
 def show_device(name):
     """Show device properties for a given device(AP or Packet Capturer).
 
@@ -136,6 +143,7 @@
         return None
     return response.json()
 
+
 def get_devices():
     """Get a list of all devices in the datastore.
 
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/__init__.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/__init__.py
index 4e32f1a..9da3529 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/__init__.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/__init__.py
@@ -52,6 +52,7 @@
 # Decorators
 def nonblocking(f):
     """Creates a decorator transforming function calls to non-blocking"""
+
     def wrap(*args, **kwargs):
         executor = ThreadPoolExecutor(max_workers=1)
         thread_future = executor.submit(f, *args, **kwargs)
@@ -65,7 +66,7 @@
 def detect_wifi_platform(dut):
     if hasattr(dut, 'wifi_platform'):
         return dut.wifi_platform
-    qcom_check = len(dut.get_file_names('/vendor/firmware/wlan/qca_cld/'))
+    qcom_check = len(dut.get_file_names('/vendor/firmware/wlan/'))
     if qcom_check:
         dut.wifi_platform = 'qcom'
     else:
@@ -74,6 +75,7 @@
 
 
 def detect_wifi_decorator(f):
+
     def wrap(*args, **kwargs):
         if 'dut' in kwargs:
             dut = kwargs['dut']
@@ -124,9 +126,7 @@
 
 
 # Miscellaneous Wifi Utilities
-def check_skip_conditions(testcase_params,
-                          dut,
-                          access_point,
+def check_skip_conditions(testcase_params, dut, access_point,
                           ota_chamber=None):
     """Checks if test should be skipped."""
     # Check battery level before test
@@ -275,7 +275,8 @@
                          socket_size=None,
                          num_processes=1,
                          udp_throughput='1000M',
-                         ipv6=False):
+                         ipv6=False,
+                         udp_length=1470):
     """Function to format iperf client arguments.
 
     This function takes in iperf client parameters and returns a properly
@@ -297,8 +298,8 @@
     if ipv6:
         iperf_args = iperf_args + '-6 '
     if traffic_type.upper() == 'UDP':
-        iperf_args = iperf_args + '-u -b {} -l 1470 -P {} '.format(
-            udp_throughput, num_processes)
+        iperf_args = iperf_args + '-u -b {} -l {} -P {} '.format(
+            udp_throughput, udp_length, num_processes)
     elif traffic_type.upper() == 'TCP':
         iperf_args = iperf_args + '-P {} '.format(num_processes)
     if socket_size:
@@ -636,7 +637,6 @@
         all reported RSSI values (signal_poll, per chain, etc.) and their
         statistics
     """
-    pass
 
 
 @nonblocking
@@ -656,7 +656,6 @@
         scan_rssi: dict containing the measurement results as well as the
         statistics of the scan RSSI for all BSSIDs in tracked_bssids
     """
-    pass
 
 
 @detect_wifi_decorator
@@ -667,13 +666,11 @@
         bdf_signature: signature consisting of last three digits of bdf cksums
         fw_signature: floating point firmware version, i.e., major.minor
     """
-    pass
 
 
 @detect_wifi_decorator
 def get_country_code(dut):
     """Function that returns the current wifi country code."""
-    pass
 
 
 @detect_wifi_decorator
@@ -688,19 +685,16 @@
         dut: dut to push bdf file to
         config_file: path to bdf_file to push
     """
-    pass
 
 
 @detect_wifi_decorator
 def start_wifi_logging(dut):
     """Function to start collecting wifi-related logs"""
-    pass
 
 
 @detect_wifi_decorator
 def stop_wifi_logging(dut):
     """Function to start collecting wifi-related logs"""
-    pass
 
 
 @detect_wifi_decorator
@@ -712,19 +706,16 @@
         firmware_files: path to wlanmdsp.mbn file
         datamsc_file: path to Data.msc file
     """
-    pass
 
 
 @detect_wifi_decorator
 def disable_beamforming(dut):
     """Function to disable beamforming."""
-    pass
 
 
 @detect_wifi_decorator
 def set_nss_capability(dut, nss):
     """Function to set number of spatial streams supported."""
-    pass
 
 
 @detect_wifi_decorator
@@ -735,11 +726,11 @@
         dut: android device
         chain_mask: desired chain mask in [0, 1, '2x2']
     """
-    pass
 
 
 # Link layer stats utilities
 class LinkLayerStats():
+
     def __new__(self, dut, llstats_enabled=True):
         if detect_wifi_platform(dut) == 'qcom':
             return qcom_utils.LinkLayerStats(dut, llstats_enabled)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/bokeh_figure.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/bokeh_figure.py
index 5a8433e..d91803b 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/bokeh_figure.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/bokeh_figure.py
@@ -19,6 +19,7 @@
 import itertools
 import json
 import math
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
 
 
 # Plotting Utilities
@@ -98,8 +99,8 @@
     def init_plot(self):
         self.plot = bokeh.plotting.figure(
             sizing_mode=self.fig_property['sizing_mode'],
-            plot_width=self.fig_property['width'],
-            plot_height=self.fig_property['height'],
+            width=self.fig_property['width'],
+            height=self.fig_property['height'],
             title=self.fig_property['title'],
             tools=self.TOOLS,
             x_axis_type=self.fig_property['x_axis_type'],
@@ -326,7 +327,7 @@
                                               figure_data=self.figure_data)
         output_file = output_file.replace('.html', '_plot_data.json')
         with open(output_file, 'w') as outfile:
-            json.dump(figure_dict, outfile, indent=4)
+            json.dump(wputils.serialize_dict(figure_dict), outfile, indent=4)
 
     def save_figure(self, output_file, save_json=True):
         """Function to save BokehFigure.
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/brcm_utils.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/brcm_utils.py
index afa5f32..fe2d3e7 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/brcm_utils.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/brcm_utils.py
@@ -378,16 +378,17 @@
     LL_STATS_CLEAR_CMD = 'wl dump_clear ampdu; wl reset_cnts;'
     BW_REGEX = re.compile(r'Chanspec:.+ (?P<bandwidth>[0-9]+)MHz')
     MCS_REGEX = re.compile(r'(?P<count>[0-9]+)\((?P<percent>[0-9]+)%\)')
-    RX_REGEX = re.compile(r'RX (?P<mode>\S+)\s+:\s*(?P<nss1>[0-9, ,(,),%]*)'
-                          '\n\s*:?\s*(?P<nss2>[0-9, ,(,),%]*)')
-    TX_REGEX = re.compile(r'TX (?P<mode>\S+)\s+:\s*(?P<nss1>[0-9, ,(,),%]*)'
-                          '\n\s*:?\s*(?P<nss2>[0-9, ,(,),%]*)')
+    RX_REGEX = re.compile(
+        r'RX (?P<mode>MCS|VHT|HE|EHT)\s+:\s*(?P<nss1>[0-9, ,(,),%]*)'
+        '\n\s*:?\s*(?P<nss2>[0-9, ,(,),%]*)')
+    TX_REGEX = re.compile(
+        r'TX (?P<mode>MCS|VHT|HE|EHT)\s+:\s*(?P<nss1>[0-9, ,(,),%]*)'
+        '\n\s*:?\s*(?P<nss2>[0-9, ,(,),%]*)')
     TX_PER_REGEX = re.compile(
         r'(?P<mode>\S+) PER\s+:\s*(?P<nss1>[0-9, ,(,),%]*)'
         '\n\s*:?\s*(?P<nss2>[0-9, ,(,),%]*)')
-    RX_FCS_REGEX = re.compile(
-        r'rxbadfcs (?P<rx_bad_fcs>[0-9]*).+\n.+goodfcs (?P<rx_good_fcs>[0-9]*)'
-    )
+    RX_GOOD_FCS_REGEX = re.compile(r'goodfcs (?P<rx_good_fcs>[0-9]*)')
+    RX_BAD_FCS_REGEX = re.compile(r'rxbadfcs (?P<rx_bad_fcs>[0-9]*)')
     RX_AGG_REGEX = re.compile(r'rxmpduperampdu (?P<aggregation>[0-9]*)')
     TX_AGG_REGEX = re.compile(r' mpduperampdu (?P<aggregation>[0-9]*)')
     TX_AGG_STOP_REGEX = re.compile(
@@ -405,6 +406,7 @@
         self.llstats_enabled = llstats_enabled
         self.llstats_cumulative = self._empty_llstats()
         self.llstats_incremental = self._empty_llstats()
+        self.bandwidth = None
 
     def update_stats(self):
         if self.llstats_enabled:
@@ -414,8 +416,9 @@
                 self.dut.adb.shell_nb(self.LL_STATS_CLEAR_CMD)
 
                 wl_join = self.dut.adb.shell("wl status")
-                self.bandwidth = int(
-                    re.search(self.BW_REGEX, wl_join).group('bandwidth'))
+                if not self.bandwidth:
+                    self.bandwidth = int(
+                        re.search(self.BW_REGEX, wl_join).group('bandwidth'))
             except:
                 llstats_output = ''
         else:
@@ -494,33 +497,32 @@
         rx_agg_match = re.search(self.RX_AGG_REGEX, llstats_output)
         tx_agg_match = re.search(self.TX_AGG_REGEX, llstats_output)
         tx_agg_stop_match = re.search(self.TX_AGG_STOP_REGEX, llstats_output)
-        rx_fcs_match = re.search(self.RX_FCS_REGEX, llstats_output)
+        rx_good_fcs_match = re.search(self.RX_GOOD_FCS_REGEX, llstats_output)
+        rx_bad_fcs_match = re.search(self.RX_BAD_FCS_REGEX, llstats_output)
 
-        if rx_agg_match and tx_agg_match and tx_agg_stop_match and rx_fcs_match:
-            agg_stop_dict = collections.OrderedDict(
-                rx_aggregation=int(rx_agg_match.group('aggregation')),
-                tx_aggregation=int(tx_agg_match.group('aggregation')),
-                tx_agg_tried=int(tx_agg_stop_match.group('agg_tried')),
-                tx_agg_canceled=int(tx_agg_stop_match.group('agg_canceled')),
-                rx_good_fcs=int(rx_fcs_match.group('rx_good_fcs')),
-                rx_bad_fcs=int(rx_fcs_match.group('rx_bad_fcs')),
-                agg_stop_reason=collections.OrderedDict())
+        mpdu_stats = collections.OrderedDict(
+            rx_aggregation=int(rx_agg_match.group('aggregation'))
+            if rx_agg_match else 0,
+            tx_aggregation=int(tx_agg_match.group('aggregation'))
+            if tx_agg_match else 0,
+            tx_agg_tried=int(tx_agg_stop_match.group('agg_tried'))
+            if tx_agg_stop_match else 0,
+            tx_agg_canceled=int(tx_agg_stop_match.group('agg_canceled'))
+            if tx_agg_stop_match else 0,
+            rx_good_fcs=int(rx_good_fcs_match.group('rx_good_fcs'))
+            if rx_good_fcs_match else 0,
+            rx_bad_fcs=int(rx_bad_fcs_match.group('rx_bad_fcs'))
+            if rx_bad_fcs_match else 0,
+            agg_stop_reason=collections.OrderedDict())
+        if tx_agg_stop_match:
             agg_reason_match = re.finditer(
                 self.TX_AGG_STOP_REASON_REGEX,
                 tx_agg_stop_match.group('agg_stop_reason'))
             for reason_match in agg_reason_match:
-                agg_stop_dict['agg_stop_reason'][reason_match.group(
+                mpdu_stats['agg_stop_reason'][reason_match.group(
                     'reason')] = reason_match.group('value')
 
-        else:
-            agg_stop_dict = collections.OrderedDict(rx_aggregation=0,
-                                                    tx_aggregation=0,
-                                                    tx_agg_tried=0,
-                                                    tx_agg_canceled=0,
-                                                    rx_good_fcs=0,
-                                                    rx_bad_fcs=0,
-                                                    agg_stop_reason=None)
-        return agg_stop_dict
+        return mpdu_stats
 
     def _generate_stats_summary(self, llstats_dict):
         llstats_summary = collections.OrderedDict(common_tx_mcs=None,
@@ -547,13 +549,17 @@
         llstats_summary['common_tx_mcs_count'] = numpy.max(tx_mpdu)
         llstats_summary['common_rx_mcs'] = mcs_ids[numpy.argmax(rx_mpdu)]
         llstats_summary['common_rx_mcs_count'] = numpy.max(rx_mpdu)
-        if sum(tx_mpdu) and sum(rx_mpdu):
+        if sum(tx_mpdu):
             llstats_summary['mean_tx_phy_rate'] = numpy.average(
                 phy_rates, weights=tx_mpdu)
-            llstats_summary['mean_rx_phy_rate'] = numpy.average(
-                phy_rates, weights=rx_mpdu)
             llstats_summary['common_tx_mcs_freq'] = (
                 llstats_summary['common_tx_mcs_count'] / sum(tx_mpdu))
+        else:
+            llstats_summary['mean_tx_phy_rate'] = 0
+            llstats_summary['common_tx_mcs_freq'] = 0
+        if sum(rx_mpdu):
+            llstats_summary['mean_rx_phy_rate'] = numpy.average(
+                phy_rates, weights=rx_mpdu)
             llstats_summary['common_rx_mcs_freq'] = (
                 llstats_summary['common_rx_mcs_count'] / sum(rx_mpdu))
             total_rx_frames = llstats_dict['mpdu_stats'][
@@ -561,7 +567,11 @@
             if total_rx_frames:
                 llstats_summary['rx_per'] = (
                     llstats_dict['mpdu_stats']['rx_bad_fcs'] /
-                    (total_rx_frames)) * 100
+                    total_rx_frames) * 100
+        else:
+            llstats_summary['mean_rx_phy_rate'] = 0
+            llstats_summary['common_rx_mcs_freq'] = 0
+            llstats_summary['rx_per'] = 0
         return llstats_summary
 
     def _update_stats(self, llstats_output):
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/qcom_utils.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/qcom_utils.py
index 53321bc..f29e4fa 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/qcom_utils.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils/qcom_utils.py
@@ -21,6 +21,7 @@
 import os
 import re
 import statistics
+import numpy
 import time
 from acts import asserts
 
@@ -275,14 +276,15 @@
 
 def _edit_dut_ini(dut, ini_fields):
     """Function to edit Wifi ini files."""
-    dut_ini_path = '/vendor/firmware/wlan/qca_cld/WCNSS_qcom_cfg.ini'
-    local_ini_path = os.path.expanduser('~/WCNSS_qcom_cfg.ini')
+    dut_ini_path = '/vendor/firmware/wlan/qcom_cfg.ini'
+    local_ini_path = os.path.expanduser('~/qcom_cfg.ini')
     dut.pull_files(dut_ini_path, local_ini_path)
 
     _set_ini_fields(local_ini_path, ini_fields)
 
     dut.push_system_file(local_ini_path, dut_ini_path)
-    dut.reboot()
+    # For 1x1 mode, we need to wait for sl4a to load (To avoid crashes)
+    dut.reboot(timeout=300, wait_after_reboot_complete=120)
 
 
 def set_chain_mask(dut, chain_mask):
@@ -336,6 +338,7 @@
 class LinkLayerStats():
 
     LLSTATS_CMD = 'cat /d/wlan0/ll_stats'
+    MOUNT_CMD = 'mount -t debugfs debugfs /sys/kernel/debug'
     PEER_REGEX = 'LL_STATS_PEER_ALL'
     MCS_REGEX = re.compile(
         r'preamble: (?P<mode>\S+), nss: (?P<num_streams>\S+), bw: (?P<bw>\S+), '
@@ -345,8 +348,8 @@
         'retries_long: (?P<retries_long>\S+)')
     MCS_ID = collections.namedtuple(
         'mcs_id', ['mode', 'num_streams', 'bandwidth', 'mcs', 'rate'])
-    MODE_MAP = {'0': '11a/g', '1': '11b', '2': '11n', '3': '11ac'}
-    BW_MAP = {'0': 20, '1': 40, '2': 80}
+    MODE_MAP = {'0': '11a/g', '1': '11b', '2': '11n', '3': '11ac', '4': '11ax'}
+    BW_MAP = {'0': 20, '1': 40, '2': 80, '3':160}
 
     def __init__(self, dut, llstats_enabled=True):
         self.dut = dut
@@ -356,6 +359,12 @@
 
     def update_stats(self):
         if self.llstats_enabled:
+            # Checking the files to see if the device is mounted to enable
+            # llstats capture
+            mount_check = len(self.dut.get_file_names('/d/wlan0'))
+            if not(mount_check):
+              self.dut.adb.shell(self.MOUNT_CMD, timeout=10)
+
             try:
                 llstats_output = self.dut.adb.shell(self.LLSTATS_CMD,
                                                     timeout=0.1)
@@ -400,7 +409,7 @@
             current_mcs = self.MCS_ID(self.MODE_MAP[match.group('mode')],
                                       int(match.group('num_streams')) + 1,
                                       self.BW_MAP[match.group('bw')],
-                                      int(match.group('mcs')),
+                                      int(match.group('mcs'), 16),
                                       int(match.group('rate'), 16) / 1000)
             current_stats = collections.OrderedDict(
                 txmpdu=int(match.group('txmpdu')),
@@ -427,9 +436,17 @@
                                                   common_rx_mcs_freq=0,
                                                   rx_per=float('nan'))
 
+        phy_rates=[]
+        tx_mpdu=[]
+        rx_mpdu=[]
         txmpdu_count = 0
         rxmpdu_count = 0
         for mcs_id, mcs_stats in llstats_dict['mcs_stats'].items():
+            # Extract the phy-rates
+            mcs_id_split=mcs_id.split();
+            phy_rates.append(float(mcs_id_split[len(mcs_id_split)-1].split('M')[0]))
+            rx_mpdu.append(mcs_stats['rxmpdu'])
+            tx_mpdu.append(mcs_stats['txmpdu'])
             if mcs_stats['txmpdu'] > llstats_summary['common_tx_mcs_count']:
                 llstats_summary['common_tx_mcs'] = mcs_id
                 llstats_summary['common_tx_mcs_count'] = mcs_stats['txmpdu']
@@ -438,6 +455,15 @@
                 llstats_summary['common_rx_mcs_count'] = mcs_stats['rxmpdu']
             txmpdu_count += mcs_stats['txmpdu']
             rxmpdu_count += mcs_stats['rxmpdu']
+
+        if len(tx_mpdu) == 0 or len(rx_mpdu) == 0:
+            return llstats_summary
+
+        # Calculate the average tx/rx -phy rates
+        if sum(tx_mpdu) and sum(rx_mpdu):
+            llstats_summary['mean_tx_phy_rate'] = numpy.average(phy_rates, weights=tx_mpdu)
+            llstats_summary['mean_rx_phy_rate'] = numpy.average(phy_rates, weights=rx_mpdu)
+
         if txmpdu_count:
             llstats_summary['common_tx_mcs_freq'] = (
                 llstats_summary['common_tx_mcs_count'] / txmpdu_count)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_power_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_power_test_utils.py
index 5c607ca..3063423 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_power_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_power_test_utils.py
@@ -33,6 +33,24 @@
 ENABLED_MODULATED_DTIM = 'gEnableModulatedDTIM='
 MAX_MODULATED_DTIM = 'gMaxLIModulatedDTIM='
 
+CHRE_WIFI_SCAN_TYPE = {
+    'active': 'active',
+    'passive': 'passive',
+    'activePassiveDfs': 'active_passive_dfs',
+    'noPreference': 'no_preference'
+}
+
+CHRE_WIFI_RADIO_CHAIN = {
+    'lowLatency': 'low_latency',
+    'lowPower': 'low_power',
+    'highAccuracy': 'high_accuracy'
+}
+
+CHRE_WIFI_CHANNEL_SET = {
+    'all': 'all',
+    'nonDfs': 'non_dfs'
+}
+
 
 def change_dtim(ad, gEnableModulatedDTIM, gMaxLIModulatedDTIM=10):
     """Function to change the DTIM setting in the phone.
@@ -118,15 +136,19 @@
         time_limit_seconds = 60
         _wait_screen_off(ad, time_limit_seconds)
 
-    old_dtim = ad.adb.shell('wl bcn_li_dtim')
+    old_dtim = _read_dtim_adb(ad)
     ad.log.info('The dtim before change is {}'.format(old_dtim))
-    if int(old_dtim) == gEnableModulatedDTIM:
-        ad.log.info('Current DTIM is already the desired value,'
-                    'no need to reset it')
-        if screen_is_on:
-            ad.log.info('Changes the screen to the original on status')
-            ad.droid.wakeUpNow()
-        return
+    try:
+        if int(old_dtim) == gEnableModulatedDTIM:
+            ad.log.info('Current DTIM is already the desired value,'
+                        'no need to reset it')
+            if screen_is_on:
+                ad.log.info('Changes the screen to the original on status')
+                ad.droid.wakeUpNow()
+            return
+    except Exception as e:
+        ad.log.info('old_dtim is not available from adb')
+
     current_dtim = _set_dtim(ad, gEnableModulatedDTIM)
     ad.log.info(
         'Old DTIM is {}, current DTIM is {}'.format(old_dtim, current_dtim))
@@ -135,9 +157,18 @@
         ad.droid.wakeUpNow()
 
 def _set_dtim(ad, gEnableModulatedDTIM):
-    ad.adb.shell("halutil -dtim_config {}".format(gEnableModulatedDTIM))
-    return ad.adb.shell('wl bcn_li_dtim')
+    out = ad.adb.shell("halutil -dtim_config {}".format(gEnableModulatedDTIM))
+    ad.log.info('set dtim to {}, stdout: {}'.format(
+        gEnableModulatedDTIM, out))
+    return _read_dtim_adb(ad)
 
+def _read_dtim_adb(ad):
+    try:
+        old_dtim = ad.adb.shell('wl bcn_li_dtim')
+        return old_dtim
+    except Exception as e:
+        ad.log.info('When reading dtim get error {}'.format(e))
+        return 'The dtim value is not available from adb'
 
 def _wait_screen_off(ad, time_limit_seconds):
     while time_limit_seconds > 0:
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/__init__.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/__init__.py
index 16f7a1d..da47cb8 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/__init__.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/__init__.py
@@ -18,11 +18,17 @@
 import copy
 import fcntl
 import importlib
+import logging
 import os
 import selenium
-import splinter
 import time
 from acts import logger
+from selenium.webdriver.support.ui import Select
+from selenium.webdriver.chrome.service import Service as ChromeService
+from selenium.webdriver.common.by import By
+from selenium.webdriver.support import expected_conditions as expected_conditions
+from webdriver_manager.chrome import ChromeDriverManager
+
 
 BROWSER_WAIT_SHORT = 1
 BROWSER_WAIT_MED = 3
@@ -106,7 +112,7 @@
         obj.teardown()
 
 
-class BlockingBrowser(splinter.driver.webdriver.chrome.WebDriver):
+class BlockingBrowser(selenium.webdriver.chrome.webdriver.WebDriver):
     """Class that implements a blocking browser session on top of selenium.
 
     The class inherits from and builds upon splinter/selenium's webdriver class
@@ -115,6 +121,7 @@
     The class is to be used within context managers (e.g. with statements) to
     ensure locks are always properly released.
     """
+
     def __init__(self, headless, timeout):
         """Constructor for BlockingBrowser class.
 
@@ -122,10 +129,14 @@
             headless: boolean to control visible/headless browser operation
             timeout: maximum time allowed to launch browser
         """
+        if int(selenium.__version__[0]) < 4:
+            raise RuntimeError(
+                'BlockingBrowser now requires selenium==4.0.0 or later. ')
         self.log = logger.create_tagged_trace_logger('ChromeDriver')
-        self.chrome_options = splinter.driver.webdriver.chrome.Options()
+        self.chrome_options = selenium.webdriver.chrome.webdriver.Options()
         self.chrome_options.add_argument('--no-proxy-server')
         self.chrome_options.add_argument('--no-sandbox')
+        self.chrome_options.add_argument('--crash-dumps-dir=/tmp')
         self.chrome_options.add_argument('--allow-running-insecure-content')
         self.chrome_options.add_argument('--ignore-certificate-errors')
         self.chrome_capabilities = selenium.webdriver.common.desired_capabilities.DesiredCapabilities.CHROME.copy(
@@ -135,7 +146,8 @@
         if headless:
             self.chrome_options.add_argument('--headless')
             self.chrome_options.add_argument('--disable-gpu')
-        self.lock_file_path = '/usr/local/bin/chromedriver'
+        os.environ['WDM_LOG'] = str(logging.NOTSET)
+        self.executable_path = ChromeDriverManager().install()
         self.timeout = timeout
 
     def __enter__(self):
@@ -146,7 +158,7 @@
         session. If an exception occurs while starting the browser, the lock
         file is released.
         """
-        self.lock_file = open(self.lock_file_path, 'r')
+        self.lock_file = open(self.executable_path, 'r')
         start_time = time.time()
         while time.time() < start_time + self.timeout:
             try:
@@ -156,14 +168,11 @@
                 continue
             try:
                 self.driver = selenium.webdriver.Chrome(
+                    service=ChromeService(self.executable_path),
                     options=self.chrome_options,
                     desired_capabilities=self.chrome_capabilities)
-                self.element_class = splinter.driver.webdriver.WebDriverElement
-                self._cookie_manager = splinter.driver.webdriver.cookie_manager.CookieManager(
-                    self.driver)
-                super(splinter.driver.webdriver.chrome.WebDriver,
-                      self).__init__(2)
-                return super(BlockingBrowser, self).__enter__()
+                self.session_id = self.driver.session_id
+                return self
             except:
                 fcntl.flock(self.lock_file, fcntl.LOCK_UN)
                 self.lock_file.close()
@@ -178,8 +187,7 @@
         releases the lock file.
         """
         try:
-            super(BlockingBrowser, self).__exit__(exc_type, exc_value,
-                                                  traceback)
+            self.driver.quit()
         except:
             raise RuntimeError('Failed to quit browser. Releasing lock file.')
         finally:
@@ -188,7 +196,7 @@
 
     def restart(self):
         """Method to restart browser session without releasing lock file."""
-        self.quit()
+        self.driver.quit()
         self.__enter__()
 
     def visit_persistent(self,
@@ -214,21 +222,25 @@
         self.driver.set_page_load_timeout(page_load_timeout)
         for idx in range(num_tries):
             try:
-                self.visit(url)
+                self.driver.get(url)
             except:
                 self.restart()
 
-            page_reached = self.url.split('/')[-1] == url.split('/')[-1]
-            if check_for_element:
-                time.sleep(BROWSER_WAIT_MED)
-                element = self.find_by_id(check_for_element)
-                if not element:
-                    page_reached = 0
+            page_reached = self.driver.current_url.split('/')[-1] == url.split(
+                '/')[-1]
             if page_reached:
-                break
+                if check_for_element:
+                    time.sleep(BROWSER_WAIT_MED)
+                    if self.is_element_visible(check_for_element):
+                        break
+                    else:
+                        raise RuntimeError(
+                            'Page reached but expected element not found.')
+                else:
+                    break
             else:
                 try:
-                    self.visit(backup_url)
+                    self.driver.get(backup_url)
                 except:
                     self.restart()
 
@@ -237,6 +249,125 @@
                     self.url))
                 raise RuntimeError('URL unreachable.')
 
+    def get_element_value(self, element_name):
+        """Function to look up and get webpage element value.
+
+        Args:
+            element_name: name of element to look up
+        Returns:
+            Value of element
+        """
+        #element = self.driver.find_element_by_name(element_name)
+        element = self.driver.find_element(By.NAME, element_name)
+        element_type = self.get_element_type(element_name)
+        if element_type == 'checkbox':
+            return element.is_selected()
+        elif element_type == 'radio':
+            items = self.driver.find_elements(By.NAME, element_name)
+            for item in items:
+                if item.is_selected():
+                    return item.get_attribute('value')
+        else:
+            return element.get_attribute('value')
+
+    def get_element_type(self, element_name):
+        """Function to look up and get webpage element type.
+
+        Args:
+            element_name: name of element to look up
+        Returns:
+            Type of element
+        """
+        item = self.driver.find_element(By.NAME, element_name)
+        type = item.get_attribute('type')
+        return type
+
+    def is_element_enabled(self, element_name):
+        """Function to check if element is enabled/interactable.
+
+        Args:
+            element_name: name of element to look up
+        Returns:
+            Boolean indicating if element is interactable
+        """
+        item = self.driver.find_element(By.NAME, element_name)
+        return item.is_enabled()
+
+    def is_element_visible(self, element_name):
+        """Function to check if element is visible.
+
+        Args:
+            element_name: name of element to look up
+        Returns:
+            Boolean indicating if element is visible
+        """
+        item = self.driver.find_element(By.NAME, element_name)
+        return item.is_displayed()
+
+    def set_element_value(self, element_name, value, select_method='value'):
+        """Function to set webpage element value.
+
+        Args:
+            element_name: name of element to set
+            value: value of element
+            select_method: select method for dropdown lists (value/index/text)
+        """
+        element_type = self.get_element_type(element_name)
+        if element_type == 'text' or element_type == 'password':
+            item = self.driver.find_element(By.NAME, element_name)
+            item.clear()
+            item.send_keys(value)
+        elif element_type == 'checkbox':
+            item = self.driver.find_element(By.NAME, element_name)
+            if value != item.is_selected():
+                item.click()
+        elif element_type == 'radio':
+            items = self.driver.find_elements(By.NAME, element_name)
+            for item in items:
+                if item.get_attribute('value') == value:
+                    item.click()
+        elif element_type == 'select-one':
+            select = Select(self.driver.find_element(By.NAME, element_name))
+            if select_method == 'value':
+                select.select_by_value(str(value))
+            elif select_method == 'text':
+                select.select_by_visible_text(value)
+            elif select_method == 'index':
+                select.select_by_index(value)
+            else:
+                raise RuntimeError(
+                    '{} is not a valid select method.'.format(select_method))
+        else:
+            raise RuntimeError(
+                'Element type {} not supported.'.format(element_type))
+
+    def click_button(self, button_name):
+        """Function to click button on webpage
+
+        Args:
+            button_name: name of button to click
+        """
+        button = self.driver.find_element(By.NAME, button_name)
+        if button.get_attribute('type') == 'submit':
+            button.click()
+        else:
+            raise RuntimeError('{} is not a button.'.format(button_name))
+
+    def accept_alert_if_present(self, wait_for_alert=1):
+        """Function to check for alert and accept if present
+
+        Args:
+            wait_for_alert: time (seconds) to wait for alert
+        """
+        try:
+            selenium.webdriver.support.ui.WebDriverWait(
+                self.driver,
+                wait_for_alert).until(expected_conditions.alert_is_present())
+            alert = self.driver.switch_to.alert
+            alert.accept()
+        except selenium.common.exceptions.TimeoutException:
+            pass
+
 
 class WifiRetailAP(object):
     """Base class implementation for retail ap.
@@ -245,6 +376,7 @@
     If some functions such as set_power not supported by ap, checks will raise
     exceptions.
     """
+
     def __init__(self, ap_settings):
         self.ap_settings = ap_settings.copy()
         self.log = logger.create_tagged_trace_logger('AccessPoint|{}'.format(
@@ -274,7 +406,6 @@
         Function implementation is AP dependent and intended to perform any
         necessary reset operations as part of controller destroy.
         """
-        pass
 
     def read_ap_settings(self):
         """Function that reads current ap settings.
@@ -548,4 +679,4 @@
                 self.lock_file.close()
                 self.log.info('Succussfully released AP lock file.')
             except:
-                raise RuntimeError('Error occurred while unlocking AP.')
\ No newline at end of file
+                raise RuntimeError('Error occurred while unlocking AP.')
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/brcm_ref.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/brcm_ref.py
index 1b09533..c05ff40 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/brcm_ref.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/brcm_ref.py
@@ -1,5 +1,6 @@
 import collections
 import numpy
+import paramiko
 import time
 from acts_contrib.test_utils.wifi.wifi_retail_ap import WifiRetailAP
 from acts_contrib.test_utils.wifi.wifi_retail_ap import BlockingBrowser
@@ -8,6 +9,8 @@
 BROWSER_WAIT_MED = 3
 BROWSER_WAIT_LONG = 10
 BROWSER_WAIT_EXTRA_LONG = 60
+SSH_WAIT_SHORT = 0.1
+SSH_READ_BYTES = 600000
 
 
 class BrcmRefAP(WifiRetailAP):
@@ -16,13 +19,45 @@
     Since most of the class' implementation is shared with the R7000, this
     class inherits from NetgearR7000AP and simply redefines config parameters
     """
+
     def __init__(self, ap_settings):
         super().__init__(ap_settings)
         self.init_gui_data()
+        # Initialize SSH connection
+        self.init_ssh_connection()
         # Read and update AP settings
         self.read_ap_settings()
         self.update_ap_settings(ap_settings)
 
+    def teardown(self):
+        """Function to perform destroy operations."""
+        if self.ap_settings.get('lock_ap', 0):
+            self._unlock_ap()
+        self.close_ssh_connection()
+
+    def init_ssh_connection(self):
+        self.ssh_client = paramiko.SSHClient()
+        self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+        self.ssh_client.connect(hostname=self.ap_settings['ip_address'],
+                                username=self.ap_settings['admin_username'],
+                                password=self.ap_settings['admin_password'],
+                                look_for_keys=False,
+                                allow_agent=False)
+
+    def close_ssh_connection(self):
+        self.ssh_client.close()
+
+    def run_ssh_cmd(self, command):
+        with self.ssh_client.invoke_shell() as shell:
+            shell.send('sh\n')
+            time.sleep(SSH_WAIT_SHORT)
+            shell.recv(SSH_READ_BYTES)
+            shell.send('{}\n'.format(command))
+            time.sleep(SSH_WAIT_SHORT)
+            response = shell.recv(SSH_READ_BYTES).decode('utf-8').splitlines()
+            response = [line for line in response[1:] if line != '# ']
+        return response
+
     def init_gui_data(self):
         self.config_page = ('{protocol}://{username}:{password}@'
                             '{ip_address}:{port}/info.html').format(
@@ -240,4 +275,146 @@
                 config_item.first.click()
                 time.sleep(BROWSER_WAIT_LONG)
                 browser.visit_persistent(self.config_page, BROWSER_WAIT_LONG,
-                                     10)
+                                         10)
+
+    def set_power(self, interface, power):
+        """Function that sets interface transmit power.
+
+        Args:
+            interface: string containing interface identifier (2G_5G, 6G)
+            power: power level in dBm
+        """
+        wl_interface = 'wl0' if interface == '6G' else 'wl1'
+
+        if power == 'auto':
+            response = self.run_ssh_cmd(
+                'wl -i {} txpwr1 -1'.format(wl_interface))
+        else:
+            power_qdbm = int(power * 4)
+            response = self.run_ssh_cmd('wl -i {} txpwr1 -o -q {}'.format(
+                wl_interface, power_qdbm))
+
+        self.ap_settings[interface]['power'] = power_qdbm / 4
+
+    def get_power(self, interface):
+        """Function to get power used by AP
+
+        Args:
+            interface: interface to get rate on (2G_5G, 6G)
+        Returns:
+            power string returned by AP.
+        """
+        wl_interface = 'wl0' if interface == '6G' else 'wl1'
+        return self.run_ssh_cmd('wl -i {} txpwr1'.format(wl_interface))
+
+    def set_rate(self,
+                 interface,
+                 mode=None,
+                 num_streams=None,
+                 rate='auto',
+                 short_gi=0,
+                 tx_expansion=0):
+        """Function that sets rate.
+
+        Args:
+            interface: string containing interface identifier (2G, 5G_1)
+            mode: string indicating the WiFi standard to use
+            num_streams: number of MIMO streams. used only for VHT
+            rate: data rate of MCS index to use
+            short_gi: boolean controlling the use of short guard interval
+        """
+        wl_interface = 'wl0' if interface == '6G' else 'wl1'
+
+        if interface == '6G':
+            band_rate = '6g_rate'
+        elif self.ap_settings['2G_5G']['channel'] < 13:
+            band_rate = '2g_rate'
+        else:
+            band_rate = '5g_rate'
+
+        if rate == 'auto':
+            cmd_string = 'wl -i {} {} auto'.format(wl_interface, band_rate)
+        elif 'legacy' in mode.lower():
+            cmd_string = 'wl -i {} {} -r {} -x {}'.format(
+                wl_interface, band_rate, rate, tx_expansion)
+        elif 'ht' in mode.lower():
+            cmd_string = 'wl -i {} {} -h {} -x {}'.format(
+                wl_interface, band_rate, rate, tx_expansion)
+            if short_gi:
+                cmd_string = cmd_string + '--sgi'
+        elif 'vht' in mode.lower():
+            cmd_string = 'wl -i {} {} -v {}x{} -x {}'.format(
+                wl_interface, band_rate, rate, num_streams, tx_expansion)
+            if short_gi:
+                cmd_string = cmd_string + '--sgi'
+        elif 'he' in mode.lower():
+            cmd_string = 'wl -i {} {} -e {}x{} -l -x {}'.format(
+                wl_interface, band_rate, rate, num_streams, tx_expansion)
+            if short_gi:
+                cmd_string = cmd_string + '-i {}'.format(short_gi)
+
+        response = self.run_ssh_cmd(cmd_string)
+
+        self.ap_settings[interface]['mode'] = mode
+        self.ap_settings[interface]['num_streams'] = num_streams
+        self.ap_settings[interface]['rate'] = rate
+        self.ap_settings[interface]['short_gi'] = short_gi
+
+    def get_rate(self, interface):
+        """Function to get rate used by AP
+
+        Args:
+            interface: interface to get rate on (2G_5G, 6G)
+        Returns:
+            rate string returned by AP.
+        """
+
+        wl_interface = 'wl0' if interface == '6G' else 'wl1'
+
+        if interface == '6G':
+            band_rate = '6g_rate'
+        elif self.ap_settings['2G_5G']['channel'] < 13:
+            band_rate = '2g_rate'
+        else:
+            band_rate = '5g_rate'
+        return self.run_ssh_cmd('wl -i {} {}'.format(wl_interface, band_rate))
+
+    def set_rts_enable(self, interface, enable):
+        """Function to enable or disable RTS/CTS
+
+        Args:
+            interface: interface to be configured (2G_5G, 6G)
+            enable: boolean controlling RTS/CTS behavior
+        """
+        wl_interface = 'wl0' if interface == '6G' else 'wl1'
+        if enable:
+            self.run_ssh_cmd('wl -i {} ampdu_rts 1'.format(wl_interface))
+            self.run_ssh_cmd('wl -i {} rtsthresh 2437'.format(wl_interface))
+        else:
+            self.run_ssh_cmd('wl -i {} ampdu_rts 0'.format(wl_interface))
+            self.run_ssh_cmd('wl -i {} rtsthresh 15000'.format(wl_interface))
+
+    def set_tx_beamformer(self, interface, enable):
+        """Function to enable or disable transmit beamforming
+
+        Args:
+            interface: interface to be configured (2G_5G, 6G)
+            enable: boolean controlling beamformer behavior
+        """
+        wl_interface = 'wl0' if interface == '6G' else 'wl1'
+
+        self.run_ssh_cmd('wl down')
+        self.run_ssh_cmd('wl -i {} txbf {}'.format(wl_interface, int(enable)))
+        self.run_ssh_cmd('wl up')
+
+    def get_sta_rssi(self, interface, sta_macaddr):
+        """Function to get RSSI from connected STA
+
+        Args:
+            interface: interface to be configured (2G_5G, 6G)
+            sta_macaddr: mac address of STA of interest
+        """
+        wl_interface = 'wl0' if interface == '6G' else 'wl1'
+
+        return self.run_ssh_cmd('wl -i {} phy_rssi_ant {}'.format(
+            wl_interface, sta_macaddr))
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/google_wifi.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/google_wifi.py
index 4023b9a..6c3230e 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/google_wifi.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/google_wifi.py
@@ -14,7 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import collections.abc
 from acts.controllers import access_point
 from acts.controllers.ap_lib import bridge_interface
 from acts.controllers.ap_lib import hostapd_security
@@ -27,6 +26,7 @@
 
     This class is a work in progress
     """
+
     def __init__(self, ap_settings):
         super().__init__(ap_settings)
         # Initialize AP
@@ -119,6 +119,10 @@
         self.access_point = access_point.AccessPoint(init_settings)
         self.configure_ap()
 
+    def teardown(self):
+        self.access_point.stop_all_aps()
+        super().teardown()
+
     def read_ap_settings(self):
         """Function that reads current ap settings."""
         return self.ap_settings.copy()
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7000.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7000.py
index ac118df..9c491a1 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7000.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7000.py
@@ -26,7 +26,11 @@
 
 class NetgearR7000AP(WifiRetailAP):
     """Class that implements Netgear R7000 AP."""
+
     def __init__(self, ap_settings):
+        self.log.warning(
+            'This AP model is no longer maintained and must be updated/verified.'
+        )
         super().__init__(ap_settings)
         self.init_gui_data()
         # Read and update AP settings
@@ -276,6 +280,7 @@
 
 class NetgearR7000NAAP(NetgearR7000AP):
     """Class that implements Netgear R7000 NA AP."""
+
     def init_gui_data(self):
         """Function to initialize data used while interacting with web GUI"""
         super().init_gui_data()
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7500.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7500.py
index 06329b6..f554e0d 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7500.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7500.py
@@ -27,7 +27,11 @@
 
 class NetgearR7500AP(WifiRetailAP):
     """Class that implements Netgear R7500 AP."""
+
     def __init__(self, ap_settings):
+        self.log.warning(
+            'This AP model is no longer maintained and must be updated/verified.'
+        )
         super().__init__(ap_settings)
         self.init_gui_data()
         # Read and update AP settings
@@ -329,7 +333,8 @@
 
 class NetgearR7500NAAP(NetgearR7500AP):
     """Class that implements Netgear R7500 NA AP."""
+
     def init_gui_data(self):
         """Function to initialize data used while interacting with web GUI"""
         super().init_gui_data()
-        self.region_map['10'] = 'North America'
\ No newline at end of file
+        self.region_map['10'] = 'North America'
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax200.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax200.py
index d6c6fad..9363bbe 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax200.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax200.py
@@ -31,6 +31,7 @@
     Since most of the class' implementation is shared with the R7000, this
     class inherits from NetgearR7000AP and simply redefines config parameters
     """
+
     def __init__(self, ap_settings):
         super().__init__(ap_settings)
         self.init_gui_data()
@@ -271,42 +272,38 @@
             # Visit URL
             browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
 
-            for key, value in self.config_page_fields.items():
-                if 'status' in key:
+            for field_key, field_name in self.config_page_fields.items():
+                if 'status' in field_key:
                     browser.visit_persistent(self.config_page_advanced,
                                              BROWSER_WAIT_MED, 10)
-                    config_item = browser.find_by_name(value)
-                    self.ap_settings[key[0]][key[1]] = int(
-                        config_item.first.checked)
+                    field_value = browser.get_element_value(field_name)
+                    self.ap_settings[field_key[0]][field_key[1]] = int(
+                        field_value)
                     browser.visit_persistent(self.config_page,
                                              BROWSER_WAIT_MED, 10)
                 else:
-                    config_item = browser.find_by_name(value)
-                    if 'enable_ax' in key:
-                        self.ap_settings[key] = int(config_item.first.checked)
-                    elif 'bandwidth' in key:
-                        self.ap_settings[key[0]][key[1]] = self.bw_mode_values[
-                            self.ap_settings['enable_ax']][
-                                config_item.first.value]
-                    elif 'power' in key:
-                        self.ap_settings[key[0]][
-                            key[1]] = self.power_mode_values[
-                                config_item.first.value]
-                    elif 'region' in key:
+                    field_value = browser.get_element_value(field_name)
+                    if 'enable_ax' in field_key:
+                        self.ap_settings[field_key] = int(field_value)
+                    elif 'bandwidth' in field_key:
+                        self.ap_settings[field_key[0]][
+                            field_key[1]] = self.bw_mode_values[
+                                self.ap_settings['enable_ax']][field_value]
+                    elif 'power' in field_key:
+                        self.ap_settings[field_key[0]][
+                            field_key[1]] = self.power_mode_values[field_value]
+                    elif 'region' in field_key:
                         self.ap_settings['region'] = self.region_map[
-                            config_item.first.value]
-                    elif 'security_type' in key:
-                        for item in config_item:
-                            if item.checked:
-                                self.ap_settings[key[0]][key[1]] = item.value
-                    elif 'channel' in key:
-                        config_item = browser.find_by_name(value)
-                        self.ap_settings[key[0]][key[1]] = int(
-                            config_item.first.value)
+                            field_value]
+                    elif 'security_type' in field_key:
+                        self.ap_settings[field_key[0]][
+                            field_key[1]] = field_value
+                    elif 'channel' in field_key:
+                        self.ap_settings[field_key[0]][field_key[1]] = int(
+                            field_value)
                     else:
-                        config_item = browser.find_by_name(value)
-                        self.ap_settings[key[0]][
-                            key[1]] = config_item.first.value
+                        self.ap_settings[field_key[0]][
+                            field_key[1]] = field_value
         return self.ap_settings.copy()
 
     def configure_ap(self, **config_flags):
@@ -323,70 +320,67 @@
                                      BROWSER_WAIT_MED, 10, self.config_page)
 
             # Update region, and power/bandwidth for each network
-            try:
-                config_item = browser.find_by_name(
-                    self.config_page_fields['region']).first
-                config_item.select_by_text(self.ap_settings['region'])
-            except:
+            if browser.is_element_enabled(self.config_page_fields['region']):
+                browser.set_element_value(self.config_page_fields['region'],
+                                          self.ap_settings['region'],
+                                          select_method='text')
+            else:
                 self.log.warning('Cannot change region.')
-            for key, value in self.config_page_fields.items():
-                if 'enable_ax' in key:
-                    config_item = browser.find_by_name(value).first
-                    if self.ap_settings['enable_ax']:
-                        config_item.check()
-                    else:
-                        config_item.uncheck()
-                if 'power' in key:
-                    config_item = browser.find_by_name(value).first
-                    config_item.select_by_text(
-                        self.ap_settings[key[0]][key[1]])
-                elif 'bandwidth' in key:
-                    config_item = browser.find_by_name(value).first
+            for field_key, field_name in self.config_page_fields.items():
+                if 'enable_ax' in field_key:
+                    browser.set_element_value(field_name,
+                                              self.ap_settings['enable_ax'])
+                if 'power' in field_key:
+                    browser.set_element_value(
+                        field_name,
+                        self.ap_settings[field_key[0]][field_key[1]],
+                        select_method='text')
+                elif 'bandwidth' in field_key:
                     try:
-                        config_item.select_by_text(self.bw_mode_text[key[0]][
-                            self.ap_settings[key[0]][key[1]]])
+                        browser.set_element_value(
+                            field_name,
+                            self.bw_mode_text[field_key[0]][self.ap_settings[
+                                field_key[0]][field_key[1]]],
+                            select_method='text')
                     except AttributeError:
                         self.log.warning(
                             'Cannot select bandwidth. Keeping AP default.')
 
             # Update security settings (passwords updated only if applicable)
-            for key, value in self.config_page_fields.items():
-                if 'security_type' in key:
-                    browser.choose(value, self.ap_settings[key[0]][key[1]])
-                    if 'WPA' in self.ap_settings[key[0]][key[1]]:
-                        config_item = browser.find_by_name(
-                            self.config_page_fields[(key[0],
-                                                     'password')]).first
-                        config_item.fill(self.ap_settings[key[0]]['password'])
+            for field_key, field_name in self.config_page_fields.items():
+                if 'security_type' in field_key:
+                    browser.set_element_value(
+                        field_name,
+                        self.ap_settings[field_key[0]][field_key[1]])
+                    if 'WPA' in self.ap_settings[field_key[0]][field_key[1]]:
+                        browser.set_element_value(
+                            self.config_page_fields[(field_key[0],
+                                                     'password')],
+                            self.ap_settings[field_key[0]]['password'])
 
-            for key, value in self.config_page_fields.items():
-                if 'ssid' in key:
-                    config_item = browser.find_by_name(value).first
-                    config_item.fill(self.ap_settings[key[0]][key[1]])
-                elif 'channel' in key:
-                    config_item = browser.find_by_name(value).first
+            for field_key, field_name in self.config_page_fields.items():
+                if 'ssid' in field_key:
+                    browser.set_element_value(
+                        field_name,
+                        self.ap_settings[field_key[0]][field_key[1]])
+                elif 'channel' in field_key:
+                    config_item = browser.find_by_name(field_name).first
                     try:
-                        config_item.select(self.ap_settings[key[0]][key[1]])
-                        time.sleep(BROWSER_WAIT_SHORT)
+                        browser.set_element_value(
+                            field_name,
+                            self.ap_settings[field_key[0]][field_key[1]])
                     except AttributeError:
                         self.log.warning(
                             'Cannot select channel. Keeping AP default.')
                     try:
                         for idx in range(0, 2):
-                            alert = browser.get_alert()
-                            alert.accept()
-                            time.sleep(BROWSER_WAIT_SHORT)
+                            browser.accept_alert_if_present(BROWSER_WAIT_SHORT)
                     except:
                         pass
             time.sleep(BROWSER_WAIT_SHORT)
-            browser.find_by_name('Apply').first.click()
+            browser.click_button('Apply')
+            browser.accept_alert_if_present(BROWSER_WAIT_SHORT)
             time.sleep(BROWSER_WAIT_SHORT)
-            try:
-                alert = browser.get_alert()
-                alert.accept()
-                time.sleep(BROWSER_WAIT_SHORT)
-            except:
-                time.sleep(BROWSER_WAIT_SHORT)
             browser.visit_persistent(self.config_page, BROWSER_WAIT_EXTRA_LONG,
                                      10)
 
@@ -400,16 +394,14 @@
                                      BROWSER_WAIT_MED, 10)
 
             # Turn radios on or off
-            for key, value in self.config_page_fields.items():
-                if 'status' in key:
-                    config_item = browser.find_by_name(value).first
-                    if self.ap_settings[key[0]][key[1]]:
-                        config_item.check()
-                    else:
-                        config_item.uncheck()
+            for field_key, field_name in self.config_page_fields.items():
+                if 'status' in field_key:
+                    browser.set_element_value(
+                        field_name,
+                        self.ap_settings[field_key[0]][field_key[1]])
 
             time.sleep(BROWSER_WAIT_SHORT)
-            browser.find_by_name('Apply').first.click()
+            browser.click_button('Apply')
             time.sleep(BROWSER_WAIT_EXTRA_LONG)
             browser.visit_persistent(self.config_page, BROWSER_WAIT_EXTRA_LONG,
                                      10)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_raxe500.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_raxe500.py
index 9dc60aa..c885e05 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_raxe500.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_raxe500.py
@@ -33,6 +33,7 @@
     Since most of the class' implementation is shared with the R7000, this
     class inherits from NetgearR7000AP and simply redefines config parameters
     """
+
     def __init__(self, ap_settings):
         super().__init__(ap_settings)
         self.init_gui_data()
@@ -173,8 +174,6 @@
             (('2G', 'bandwidth'), 'opmode'),
             (('5G_1', 'bandwidth'), 'opmode_an'),
             (('6G', 'bandwidth'), 'opmode_an_2'),
-            (('2G', 'power'), 'enable_tpc'),
-            (('5G_1', 'power'), 'enable_tpc_an'),
             (('6G', 'security_type'), 'security_type_an_2'),
             (('5G_1', 'security_type'), 'security_type_an'),
             (('2G', 'security_type'), 'security_type'),
@@ -183,13 +182,6 @@
             (('6G', 'password'), 'passphrase_an_2')
         ])
 
-        self.power_mode_values = {
-            '1': '100%',
-            '2': '75%',
-            '3': '50%',
-            '4': '25%'
-        }
-
     def _set_channel_and_bandwidth(self,
                                    network,
                                    channel=None,
@@ -286,7 +278,9 @@
             browser.visit_persistent(self.firmware_page, BROWSER_WAIT_MED, 10)
             firmware_regex = re.compile(
                 r'Firmware Version[\s\S]+V(?P<version>[0-9._]+)')
-            firmware_version = re.search(firmware_regex, browser.html)
+            #firmware_version = re.search(firmware_regex, browser.html)
+            firmware_version = re.search(firmware_regex,
+                                         browser.driver.page_source)
             if firmware_version:
                 self.ap_settings['firmware_version'] = firmware_version.group(
                     'version')
@@ -300,42 +294,35 @@
             # Visit URL
             browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
 
-            for key, value in self.config_page_fields.items():
-                if 'status' in key:
+            for field_key, field_name in self.config_page_fields.items():
+                if 'status' in field_key:
                     browser.visit_persistent(self.config_page_advanced,
                                              BROWSER_WAIT_MED, 10)
-                    config_item = browser.find_by_name(value)
-                    self.ap_settings[key[0]][key[1]] = int(
-                        config_item.first.checked)
+                    field_value = browser.get_element_value(field_name)
+                    self.ap_settings[field_key[0]][field_key[1]] = int(
+                        field_value)
                     browser.visit_persistent(self.config_page,
                                              BROWSER_WAIT_MED, 10)
                 else:
-                    config_item = browser.find_by_name(value)
-                    if 'enable_ax' in key:
-                        self.ap_settings[key] = int(config_item.first.checked)
-                    elif 'bandwidth' in key:
-                        self.ap_settings[key[0]][key[1]] = self.bw_mode_values[
-                            self.ap_settings['enable_ax']][
-                                config_item.first.value]
-                    elif 'power' in key:
-                        self.ap_settings[key[0]][
-                            key[1]] = self.power_mode_values[
-                                config_item.first.value]
-                    elif 'region' in key:
+                    field_value = browser.get_element_value(field_name)
+                    if 'enable_ax' in field_key:
+                        self.ap_settings[field_key] = int(field_value)
+                    elif 'bandwidth' in field_key:
+                        self.ap_settings[field_key[0]][
+                            field_key[1]] = self.bw_mode_values[
+                                self.ap_settings['enable_ax']][field_value]
+                    elif 'region' in field_key:
                         self.ap_settings['region'] = self.region_map[
-                            config_item.first.value]
-                    elif 'security_type' in key:
-                        for item in config_item:
-                            if item.checked:
-                                self.ap_settings[key[0]][key[1]] = item.value
-                    elif 'channel' in key:
-                        config_item = browser.find_by_name(value)
-                        self.ap_settings[key[0]][key[1]] = int(
-                            config_item.first.value)
+                            field_value]
+                    elif 'security_type' in field_key:
+                        self.ap_settings[field_key[0]][
+                            field_key[1]] = field_value
+                    elif 'channel' in field_key:
+                        self.ap_settings[field_key[0]][field_key[1]] = int(
+                            field_value)
                     else:
-                        config_item = browser.find_by_name(value)
-                        self.ap_settings[key[0]][
-                            key[1]] = config_item.first.value
+                        self.ap_settings[field_key[0]][
+                            field_key[1]] = field_value
         return self.ap_settings.copy()
 
     def configure_ap(self, **config_flags):
@@ -352,68 +339,58 @@
                                      BROWSER_WAIT_MED, 10, self.config_page)
 
             # Update region, and power/bandwidth for each network
-            try:
-                config_item = browser.find_by_name(
-                    self.config_page_fields['region']).first
-                config_item.select_by_text(self.ap_settings['region'])
-            except:
+            if browser.is_element_enabled(self.config_page_fields['region']):
+                browser.set_element_value(self.config_page_fields['region'],
+                                          self.ap_settings['region'],
+                                          select_method='text')
+            else:
                 self.log.warning('Cannot change region.')
-            for key, value in self.config_page_fields.items():
-                if 'enable_ax' in key:
-                    config_item = browser.find_by_name(value).first
-                    if self.ap_settings['enable_ax']:
-                        config_item.check()
-                    else:
-                        config_item.uncheck()
-                if 'power' in key:
-                    config_item = browser.find_by_name(value).first
-                    config_item.select_by_text(
-                        self.ap_settings[key[0]][key[1]])
-                elif 'bandwidth' in key:
-                    config_item = browser.find_by_name(value).first
+            for field_key, field_name in self.config_page_fields.items():
+                if 'enable_ax' in field_key:
+                    browser.set_element_value(field_name,
+                                              self.ap_settings['enable_ax'])
+                elif 'bandwidth' in field_key:
                     try:
-                        config_item.select_by_text(self.bw_mode_text[key[0]][
-                            self.ap_settings[key[0]][key[1]]])
+                        browser.set_element_value(
+                            field_name,
+                            self.bw_mode_text[field_key[0]][self.ap_settings[
+                                field_key[0]][field_key[1]]],
+                            select_method='text')
                     except AttributeError:
                         self.log.warning(
                             'Cannot select bandwidth. Keeping AP default.')
 
             # Update security settings (passwords updated only if applicable)
-            for key, value in self.config_page_fields.items():
-                if 'security_type' in key:
-                    browser.choose(value, self.ap_settings[key[0]][key[1]])
-                    if 'WPA' in self.ap_settings[key[0]][key[1]]:
-                        config_item = browser.find_by_name(
-                            self.config_page_fields[(key[0],
-                                                     'password')]).first
-                        config_item.fill(self.ap_settings[key[0]]['password'])
+            for field_key, field_name in self.config_page_fields.items():
+                if 'security_type' in field_key:
+                    browser.set_element_value(
+                        field_name,
+                        self.ap_settings[field_key[0]][field_key[1]])
+                    if 'WPA' in self.ap_settings[field_key[0]][field_key[1]]:
+                        browser.set_element_value(
+                            self.config_page_fields[(field_key[0],
+                                                     'password')],
+                            self.ap_settings[field_key[0]]['password'])
 
-            for key, value in self.config_page_fields.items():
-                if 'ssid' in key:
-                    config_item = browser.find_by_name(value).first
-                    config_item.fill(self.ap_settings[key[0]][key[1]])
-                elif 'channel' in key:
-                    config_item = browser.find_by_name(value).first
+            for field_key, field_name in self.config_page_fields.items():
+                if 'ssid' in field_key:
+                    browser.set_element_value(
+                        field_name,
+                        self.ap_settings[field_key[0]][field_key[1]])
+                elif 'channel' in field_key:
                     try:
-                        config_item.select(self.ap_settings[key[0]][key[1]])
-                        time.sleep(BROWSER_WAIT_SHORT)
+                        browser.set_element_value(
+                            field_name,
+                            self.ap_settings[field_key[0]][field_key[1]])
                     except AttributeError:
                         self.log.warning(
                             'Cannot select channel. Keeping AP default.')
-                    try:
-                        alert = browser.get_alert()
-                        alert.accept()
-                    except:
-                        pass
+                    browser.accept_alert_if_present(BROWSER_WAIT_SHORT)
+
             time.sleep(BROWSER_WAIT_SHORT)
-            browser.find_by_name('Apply').first.click()
+            browser.click_button('Apply')
+            browser.accept_alert_if_present(BROWSER_WAIT_SHORT)
             time.sleep(BROWSER_WAIT_SHORT)
-            try:
-                alert = browser.get_alert()
-                alert.accept()
-                time.sleep(BROWSER_WAIT_SHORT)
-            except:
-                time.sleep(BROWSER_WAIT_SHORT)
             browser.visit_persistent(self.config_page, BROWSER_WAIT_EXTRA_LONG,
                                      10)
 
@@ -427,16 +404,14 @@
                                      BROWSER_WAIT_MED, 10)
 
             # Turn radios on or off
-            for key, value in self.config_page_fields.items():
-                if 'status' in key:
-                    config_item = browser.find_by_name(value).first
-                    if self.ap_settings[key[0]][key[1]]:
-                        config_item.check()
-                    else:
-                        config_item.uncheck()
+            for field_key, field_name in self.config_page_fields.items():
+                if 'status' in field_key:
+                    browser.set_element_value(
+                        field_name,
+                        self.ap_settings[field_key[0]][field_key[1]])
 
             time.sleep(BROWSER_WAIT_SHORT)
-            browser.find_by_name('Apply').first.click()
+            browser.click_button('Apply')
             time.sleep(BROWSER_WAIT_EXTRA_LONG)
             browser.visit_persistent(self.config_page, BROWSER_WAIT_EXTRA_LONG,
                                      10)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_test_utils.py
index d9ed9c0..6f953c5 100755
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_test_utils.py
@@ -56,9 +56,6 @@
 
 DEFAULT_PING_ADDR = "https://www.google.com/robots.txt"
 
-CNSS_DIAG_CONFIG_PATH = "/data/vendor/wifi/cnss_diag/"
-CNSS_DIAG_CONFIG_FILE = "cnss_diag.conf"
-
 ROAMING_ATTN = {
     "AP1_on_AP2_off": [0, 0, 95, 95],
     "AP1_off_AP2_on": [95, 95, 0, 0],
@@ -811,7 +808,7 @@
             break
 
 
-def wifi_test_device_init(ad):
+def wifi_test_device_init(ad, country_code=WifiEnums.CountryCode.US):
     """Initializes an android device for wifi testing.
 
     0. Make sure SL4A connection is established on the android device.
@@ -842,7 +839,7 @@
     ad.log.info("wpa_supplicant log change status: %s", output)
     utils.sync_device_time(ad)
     ad.droid.telephonyToggleDataConnection(False)
-    set_wifi_country_code(ad, WifiEnums.CountryCode.US)
+    set_wifi_country_code(ad, country_code)
     utils.set_ambient_display(ad, False)
 
 
@@ -2602,73 +2599,63 @@
     asserts.fail("Did not find MAC = %s in packet sniffer."
                  "for device %s" % (mac, ad.serial))
 
-
-def start_cnss_diags(ads, cnss_diag_file, pixel_models):
+def start_all_wlan_logs(ads):
     for ad in ads:
-        start_cnss_diag(ad, cnss_diag_file, pixel_models)
+        start_wlan_logs(ad)
 
-
-def start_cnss_diag(ad, cnss_diag_file, pixel_models):
-    """Start cnss_diag to record extra wifi logs
+def start_wlan_logs(ad):
+    """Start Pixel Logger to record extra wifi logs
 
     Args:
         ad: android device object.
-        cnss_diag_file: cnss diag config file to push to device.
-        pixel_models: pixel devices.
     """
-    if ad.model not in pixel_models:
-        ad.log.info("Device not supported to collect pixel logger")
+    if not ad.adb.shell("pm list package | grep com.android.pixellogger"):
+        ad.log.info("Device doesn't have Pixel Logger")
         return
-    if ad.model in wifi_constants.DEVICES_USING_LEGACY_PROP:
-        prop = wifi_constants.LEGACY_CNSS_DIAG_PROP
-    else:
-        prop = wifi_constants.CNSS_DIAG_PROP
-    if ad.adb.getprop(prop) != 'true':
-        if not int(
-                ad.adb.shell("ls -l %s%s | wc -l" %
-                             (CNSS_DIAG_CONFIG_PATH, CNSS_DIAG_CONFIG_FILE))):
-            ad.adb.push("%s %s" % (cnss_diag_file, CNSS_DIAG_CONFIG_PATH))
-        ad.adb.shell(
-            "find /data/vendor/wifi/cnss_diag/wlan_logs/ -type f -delete",
+
+    ad.adb.shell(
+            "find /sdcard/Android/data/com.android.pixellogger/files/logs"
+            "/wlan_logs/ -type f -delete",
             ignore_status=True)
-        ad.adb.shell("setprop %s true" % prop, ignore_status=True)
-
-
-def stop_cnss_diags(ads, pixel_models):
-    for ad in ads:
-        stop_cnss_diag(ad, pixel_models)
-
-
-def stop_cnss_diag(ad, pixel_models):
-    """Stops cnss_diag
-
-    Args:
-        ad: android device object.
-        pixel_models: pixel devices.
-    """
-    if ad.model not in pixel_models:
-        ad.log.info("Device not supported to collect pixel logger")
-        return
-    if ad.model in wifi_constants.DEVICES_USING_LEGACY_PROP:
-        prop = wifi_constants.LEGACY_CNSS_DIAG_PROP
+    if ad.file_exists("/vendor/bin/cnss_diag"):
+        ad.adb.shell("am startservice -a com.android.pixellogger.service"
+                ".logging.LoggingService.ACTION_START_LOGGING "
+                "-e intent_logger cnss_diag", ignore_status=True)
     else:
-        prop = wifi_constants.CNSS_DIAG_PROP
-    ad.adb.shell("setprop %s false" % prop, ignore_status=True)
+        ad.adb.shell("am startservice -a com.android.pixellogger.service"
+                ".logging.LoggingService.ACTION_START_LOGGING "
+                "-e intent_logger wlan_logs", ignore_status=True)
 
+def stop_all_wlan_logs(ads):
+    for ad in ads:
+        stop_wlan_logs(ad)
+    ad.log.info("Wait 30s for the createion of zip file for wlan logs")
+    time.sleep(30)
 
-def get_cnss_diag_log(ad):
-    """Pulls the cnss_diag logs in the wlan_logs dir
+def stop_wlan_logs(ad):
+    """Stops Pixel Logger
+
     Args:
         ad: android device object.
     """
-    logs = ad.get_file_names("/data/vendor/wifi/cnss_diag/wlan_logs/")
+    if not ad.adb.shell("pm list package | grep com.android.pixellogger"):
+        return
+
+    ad.adb.shell("am startservice -a com.android.pixellogger.service.logging"
+            ".LoggingService.ACTION_STOP_LOGGING", ignore_status=True)
+
+def get_wlan_logs(ad):
+    """Pull logs from Pixel Logger folder
+    Args:
+        ad: android device object.
+    """
+    logs = ad.get_file_names("/sdcard/Android/data/com.android.pixellogger/files/logs/wlan_logs")
     if logs:
-        ad.log.info("Pulling cnss_diag logs %s", logs)
-        log_path = os.path.join(ad.device_log_path, "CNSS_DIAG_%s" % ad.serial)
+        ad.log.info("Pulling Pixel Logger logs %s", logs)
+        log_path = os.path.join(ad.device_log_path, "WLAN_LOGS_%s" % ad.serial)
         os.makedirs(log_path, exist_ok=True)
         ad.pull_files(logs, log_path)
 
-
 LinkProbeResult = namedtuple(
     'LinkProbeResult',
     ('is_success', 'stdout', 'elapsed_time', 'failure_reason'))
@@ -2944,3 +2931,38 @@
                              timeout=20),
         "%s ping %s failed" % (dut2.serial, dut1_ip))
 
+def get_wear_wifimediator_disable_status(ad):
+    """Gets WearWifiMediator disable status.
+
+    Args:
+        ad: Android Device
+
+    Returns:
+        True if WearWifiMediator is disabled, False otherwise.
+    """
+    status = ad.adb.shell("settings get global cw_disable_wifimediator")
+    if status == "1":
+        ad.log.info("WearWifiMediator is DISABLED")
+        status = True
+    else:
+        ad.log.info("WearWifiMediator is ENABLED")
+        status = False
+    return status
+
+def disable_wear_wifimediator(ad, state):
+    """Disables WearWifiMediator.
+
+    Args:
+        ad: Android Device
+        state: True to disable, False otherwise.
+    """
+    if state:
+        ad.log.info("Disabling WearWifiMediator.....")
+        ad.adb.shell("settings put global cw_disable_wifimediator 1")
+        asserts.assert_true(get_wear_wifimediator_disable_status(ad),
+                            "WearWifiMediator should be disabled")
+    else:
+        ad.log.info("Enabling WearWifiMediator.....")
+        ad.adb.shell("settings put global cw_disable_wifimediator 0")
+        asserts.assert_false(get_wear_wifimediator_disable_status(ad),
+                             "WearWifiMediator should be enabled")
diff --git a/acts_tests/setup.py b/acts_tests/setup.py
index b2c3151..8da7808 100755
--- a/acts_tests/setup.py
+++ b/acts_tests/setup.py
@@ -30,7 +30,12 @@
 
 acts_tests_dir = os.path.abspath(os.path.dirname(__file__))
 
-install_requires = ['soundfile']
+install_requires = [
+    # Require an older version of setuptools that does not enforce PEP 440.
+    # This must be added first.
+    'setuptools<66.0.0',
+    'soundfile'
+]
 
 if sys.version_info < (3, 6):
     # Python <= 3.5 uses bokeh up to 1.4.x
@@ -79,13 +84,10 @@
     Otherwise, it will attempt to locate the ACTS framework from the local
     repository.
     """
-
-    def run(self):
+    def do_egg_install(self):
+        # Ref. https://stackoverflow.com/a/20196065
         _setup_acts_framework('install')
-        # Calling install.run() directly fails to install the dependencies as
-        # listed in install_requires. Use install.do_egg_install() instead.
-        # Ref: https://stackoverflow.com/questions/21915469
-        self.do_egg_install()
+        install.do_egg_install(self)
 
 
 class ActsContribDevelop(develop):
@@ -102,36 +104,6 @@
             _setup_acts_framework('develop')
 
 
-class ActsContribInstallDependencies(cmd.Command):
-    """Installs only required packages
-
-    Installs all required packages for acts_contrib to work. Rather than using
-    the normal install system which creates links with the python egg, pip is
-    used to install the packages.
-    """
-
-    description = 'Install dependencies needed for acts_contrib packages.'
-    user_options = []
-
-    def initialize_options(self):
-        pass
-
-    def finalize_options(self):
-        pass
-
-    def run(self):
-        install_args = [sys.executable, '-m', 'pip', 'install']
-        subprocess.check_call(install_args + ['--upgrade', 'pip'])
-        required_packages = self.distribution.install_requires
-
-        for package in required_packages:
-            self.announce('Installing %s...' % package, log.INFO)
-            subprocess.check_call(install_args +
-                                  ['-v', '--no-cache-dir', package])
-
-        self.announce('Dependencies installed.')
-
-
 class ActsContribUninstall(cmd.Command):
     """acts_contrib uninstaller.
 
@@ -209,7 +181,6 @@
                      cmdclass={
                          'install': ActsContribInstall,
                          'develop': ActsContribDevelop,
-                         'install_deps': ActsContribInstallDependencies,
                          'uninstall': ActsContribUninstall
                      },
                      url="http://www.android.com/")
diff --git a/acts_tests/tests/OWNERS b/acts_tests/tests/OWNERS
index 3f4ab0f..6fc1e96 100644
--- a/acts_tests/tests/OWNERS
+++ b/acts_tests/tests/OWNERS
@@ -12,10 +12,14 @@
 codycaldwell@google.com
 chaoyangf@google.com
 wju@google.com
+hmtuan@google.com
+jethier@google.com
 
 # Pixel GTW
 jasonkmlu@google.com
 hongscott@google.com
+diegowchung@google.com
+kuoyuanchiang@google.com
 markusliu@google.com
 jerrypcchen@google.com
 martschneider@google.com
diff --git a/acts_tests/tests/google/ble/api/BleAdvertiseApiTest.py b/acts_tests/tests/google/ble/api/BleAdvertiseApiTest.py
index 877b00e..4c05e48 100644
--- a/acts_tests/tests/google/ble/api/BleAdvertiseApiTest.py
+++ b/acts_tests/tests/google/ble/api/BleAdvertiseApiTest.py
@@ -20,8 +20,6 @@
 then other test suites utilising Ble Advertisements will also fail.
 """
 
-import pprint
-
 from acts.controllers.sl4a_lib import rpc_client
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
@@ -496,8 +494,9 @@
         self.log.debug("Step 1: Setup environment.")
         droid = self.ad_dut.droid
         exp_is_connectable = False
-        self.log.debug("Step 2: Set the filtering settings object's value to "
-                       + str(exp_is_connectable))
+        self.log.debug(
+            "Step 2: Set the filtering settings object's value to " +
+            str(exp_is_connectable))
         return self.verify_adv_settings_is_connectable(droid,
                                                        exp_is_connectable)
 
@@ -531,7 +530,8 @@
         self.log.debug(
             "Step 2: Set the filtering settings object's value to {}".format(
                 exp_adv_own_address_type))
-        return self.verify_adv_settings_own_address_type(droid, exp_adv_own_address_type)
+        return self.verify_adv_settings_own_address_type(
+            droid, exp_adv_own_address_type)
 
     @BluetoothBaseTest.bt_test_wrap
     def test_adv_settings_set_adv_own_address_type_random(self):
@@ -563,7 +563,8 @@
         self.log.debug(
             "Step 2: Set the filtering settings object's value to {}".format(
                 exp_adv_own_address_type))
-        return self.verify_adv_settings_own_address_type(droid, exp_adv_own_address_type)
+        return self.verify_adv_settings_own_address_type(
+            droid, exp_adv_own_address_type)
 
     @BluetoothBaseTest.bt_test_wrap
     def test_adv_with_multiple_own_address_types(self):
@@ -579,8 +580,8 @@
             service_data.append(i)
 
         for own_add_type in exp_adv_own_address_types:
-            result = self.verify_adv_set_address_type_start_adv(ad_droid,
-                    own_add_type, uuid, service_data)
+            result = self.verify_adv_set_address_type_start_adv(
+                ad_droid, own_add_type, uuid, service_data)
             if result is False:
                 return False
 
@@ -589,8 +590,7 @@
         filter_list = sc_droid.bleGenFilterList()
         scan_settings = sc_droid.bleBuildScanSetting()
         scan_callback = sc_droid.bleGenScanCallback()
-        sc_droid.bleStartBleScan(filter_list, scan_settings,
-                                       scan_callback)
+        sc_droid.bleStartBleScan(filter_list, scan_settings, scan_callback)
         event_name = scan_result.format(scan_callback)
         self.log.info("Scan onSuccess Event")
 
@@ -606,7 +606,8 @@
                 if uuid in serviceUuidList:
                     mac_addr = deviceInfo['address']
                     if mac_addr not in mac_list:
-                        self.log.info("Found device. address: {}".format(mac_addr))
+                        self.log.info(
+                            "Found device. address: {}".format(mac_addr))
                         mac_list.append(mac_addr)
 
             except rpc_client.Sl4aApiError:
@@ -641,8 +642,10 @@
         self.log.debug("Step 1: Setup environment.")
         droid = self.ad_dut.droid
         exp_adv_own_address_type = -100
-        self.log.debug("Step 2: Set the filtering settings own address type to -1")
-        return self.verify_invalid_adv_settings_own_address_type(droid, exp_adv_own_address_type)
+        self.log.debug(
+            "Step 2: Set the filtering settings own address type to -1")
+        return self.verify_invalid_adv_settings_own_address_type(
+            droid, exp_adv_own_address_type)
 
     @BluetoothBaseTest.bt_test_wrap
     @test_tracker_info(uuid='a770ed7e-c6cd-4533-8876-e42e68f8b4fb')
@@ -1104,8 +1107,8 @@
         droid = self.ad_dut.droid
         exp_include_device_name = True
         self.log.debug(
-            "Step 2: Set the filtering data object's include device name: {}"
-            .format(exp_include_device_name))
+            "Step 2: Set the filtering data object's include device name: {}".
+            format(exp_include_device_name))
         return self.verify_adv_data_include_device_name(
             droid, exp_include_device_name)
 
@@ -1242,9 +1245,11 @@
                        " value test Passed.".format(exp_is_connectable))
         return True
 
-    def verify_adv_settings_own_address_type(self, droid, exp_adv_own_address_type):
+    def verify_adv_settings_own_address_type(self, droid,
+                                             exp_adv_own_address_type):
         try:
-            droid.bleSetAdvertiseSettingsOwnAddressType(exp_adv_own_address_type)
+            droid.bleSetAdvertiseSettingsOwnAddressType(
+                exp_adv_own_address_type)
         except BleAdvertiseVerificationError as error:
             self.log.debug(str(error))
             return False
@@ -1261,15 +1266,15 @@
                        "  value test Passed.".format(exp_adv_own_address_type))
         return True
 
-    def verify_adv_set_address_type_start_adv(self, droid, own_address_type, uuid, service_data):
+    def verify_adv_set_address_type_start_adv(self, droid, own_address_type,
+                                              uuid, service_data):
         try:
             droid.bleSetAdvertiseSettingsOwnAddressType(own_address_type)
         except BleAdvertiseVerificationError as error:
             self.log.debug(str(error))
             return False
 
-        droid.bleAddAdvertiseDataServiceData(
-            uuid, service_data)
+        droid.bleAddAdvertiseDataServiceData(uuid, service_data)
         advcallback, adv_data, adv_settings = generate_ble_advertise_objects(
             droid)
         droid.bleStartBleAdvertising(advcallback, adv_data, adv_settings)
@@ -1282,7 +1287,8 @@
             return False
 
         try:
-            event = self.android_devices[0].ed.pop_event(adv_succ.format(advcallback), 10)
+            event = self.android_devices[0].ed.pop_event(
+                adv_succ.format(advcallback), 10)
             self.log.info("Ble Advertise Success Event: {}".format(event))
         except rpc_client.Sl4aApiError:
             self.log.info("{} event was not found.".format(
@@ -1372,8 +1378,9 @@
             self.log.debug("exp value: " + str(exp_include_tx_power_level) +
                            ", Actual value: " + str(include_tx_power_level))
             return False
-        self.log.debug("Advertise Setting's include tx power level " + str(
-            exp_include_tx_power_level) + "  value test Passed.")
+        self.log.debug("Advertise Setting's include tx power level " +
+                       str(exp_include_tx_power_level) +
+                       "  value test Passed.")
         return True
 
     def verify_adv_data_include_device_name(self, droid,
@@ -1424,17 +1431,19 @@
             return True
 
     def verify_invalid_adv_settings_own_address_type(self, droid,
-                                                   exp_adv_own_address_type):
+                                                     exp_adv_own_address_type):
         try:
-            droid.bleSetAdvertiseSettingsOwnAddressType(exp_adv_own_address_type)
+            droid.bleSetAdvertiseSettingsOwnAddressType(
+                exp_adv_own_address_type)
             droid.bleBuildAdvertiseSettings()
-            self.log.debug("Set Advertise settings invalid own address type " +
-                           " with input as {}".format(exp_adv_own_address_type))
+            self.log.debug(
+                "Set Advertise settings invalid own address type " +
+                " with input as {}".format(exp_adv_own_address_type))
             return False
         except rpc_client.Sl4aApiError:
-            self.log.debug(
-                "Set Advertise settings invalid own address type "
-                "failed successfully with input as {}".format(exp_adv_own_address_type))
+            self.log.debug("Set Advertise settings invalid own address type "
+                           "failed successfully with input as {}".format(
+                               exp_adv_own_address_type))
             return True
 
     def verify_invalid_adv_data_service_uuids(self, droid, exp_service_uuids):
@@ -1450,8 +1459,9 @@
                 "successfully with input as {}".format(exp_service_uuids))
             return True
 
-    def verify_invalid_adv_data_service_data(
-            self, droid, exp_service_data_uuid, exp_service_data):
+    def verify_invalid_adv_data_service_data(self, droid,
+                                             exp_service_data_uuid,
+                                             exp_service_data):
         try:
             droid.bleAddAdvertiseDataServiceData(exp_service_data_uuid,
                                                  exp_service_data)
@@ -1472,9 +1482,9 @@
             droid.bleAddAdvertiseDataManufacturerId(exp_manu_id,
                                                     exp_manu_specific_data)
             droid.bleBuildAdvertiseData()
-            self.log.debug(
-                "Set Advertise Data manu id: " + str(exp_manu_id) +
-                ", manu specific data: " + str(exp_manu_specific_data))
+            self.log.debug("Set Advertise Data manu id: " + str(exp_manu_id) +
+                           ", manu specific data: " +
+                           str(exp_manu_specific_data))
             return False
         except rpc_client.Sl4aApiError:
             self.log.debug("Set Advertise Data manu id: {},"
diff --git a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py
index 2e2be83..a679b56 100644
--- a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py
+++ b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py
@@ -18,11 +18,6 @@
 Bluetooth stack.
 """
 
-import concurrent
-import os
-import time
-
-from queue import Empty
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.bt_constants import ble_advertise_settings_modes
@@ -105,8 +100,8 @@
         self.scn_ad.droid.bleBuildScanFilter(filter_list)
         self.scn_ad.droid.bleSetScanSettingsCallbackType(
             ble_scan_settings_callback_types['all_matches'])
-        self.scn_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
-            'low_latency'])
+        self.scn_ad.droid.bleSetScanSettingsScanMode(
+            ble_scan_settings_modes['low_latency'])
         iterations = 20
         for _ in range(iterations):
             self.log.info("Verify all advertisements found")
diff --git a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
index 7fd0d52..126dba8 100644
--- a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
+++ b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py
@@ -19,7 +19,6 @@
 """
 
 import concurrent
-import os
 import time
 
 from queue import Empty
@@ -67,8 +66,7 @@
             scan_and_verify_n_advertisements(self.scn_ad, num_advertisements)
         except BtTestUtilsError:
             return False
-        teardown_n_advertisements(self.adv_ad,
-                                  len(advertise_callback_list),
+        teardown_n_advertisements(self.adv_ad, len(advertise_callback_list),
                                   advertise_callback_list)
         return True
 
@@ -414,30 +412,32 @@
         test_result = True
         advertise_callback, advertise_data, advertise_settings = (
             generate_ble_advertise_objects(self.adv_ad.droid))
-        self.adv_ad.droid.bleStartBleAdvertising(
-            advertise_callback, advertise_data, advertise_settings)
+        self.adv_ad.droid.bleStartBleAdvertising(advertise_callback,
+                                                 advertise_data,
+                                                 advertise_settings)
         try:
-            self.adv_ad.ed.pop_event(
-                adv_succ.format(advertise_callback), self.default_timeout)
+            self.adv_ad.ed.pop_event(adv_succ.format(advertise_callback),
+                                     self.default_timeout)
         except Empty as error:
             self.log.error("Test failed with Empty error: {}".format(error))
             return False
         except concurrent.futures._base.TimeoutError as error:
             self.log.debug(
-                "Test failed, filtering callback onSuccess never occurred: {}"
-                .format(error))
+                "Test failed, filtering callback onSuccess never occurred: {}".
+                format(error))
         try:
-            self.adv_ad.droid.bleStartBleAdvertising(
-                advertise_callback, advertise_data, advertise_settings)
-            self.adv_ad.ed.pop_event(
-                adv_succ.format(advertise_callback), self.default_timeout)
+            self.adv_ad.droid.bleStartBleAdvertising(advertise_callback,
+                                                     advertise_data,
+                                                     advertise_settings)
+            self.adv_ad.ed.pop_event(adv_succ.format(advertise_callback),
+                                     self.default_timeout)
             test_result = False
         except Empty as error:
             self.log.debug("Test passed with Empty error: {}".format(error))
         except concurrent.futures._base.TimeoutError as error:
             self.log.debug(
-                "Test passed, filtering callback onSuccess never occurred: {}"
-                .format(error))
+                "Test passed, filtering callback onSuccess never occurred: {}".
+                format(error))
 
         return test_result
 
@@ -471,11 +471,12 @@
         self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
         advertise_callback, advertise_data, advertise_settings = (
             generate_ble_advertise_objects(self.adv_ad.droid))
-        self.adv_ad.droid.bleStartBleAdvertising(
-            advertise_callback, advertise_data, advertise_settings)
+        self.adv_ad.droid.bleStartBleAdvertising(advertise_callback,
+                                                 advertise_data,
+                                                 advertise_settings)
         try:
-            self.adv_ad.ed.pop_event(
-                adv_succ.format(advertise_callback), self.default_timeout)
+            self.adv_ad.ed.pop_event(adv_succ.format(advertise_callback),
+                                     self.default_timeout)
         except Empty as error:
             self.log.error("Test failed with Empty error: {}".format(error))
             return False
@@ -493,8 +494,8 @@
         self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
                                           scan_callback)
         try:
-            self.scn_ad.ed.pop_event(
-                scan_result.format(scan_callback), self.default_timeout)
+            self.scn_ad.ed.pop_event(scan_result.format(scan_callback),
+                                     self.default_timeout)
         except Empty as error:
             self.log.error("Test failed with: {}".format(error))
             return False
@@ -546,11 +547,12 @@
         test_result = True
         advertise_callback, advertise_data, advertise_settings = (
             generate_ble_advertise_objects(self.adv_ad.droid))
-        self.adv_ad.droid.bleStartBleAdvertising(
-            advertise_callback, advertise_data, advertise_settings)
+        self.adv_ad.droid.bleStartBleAdvertising(advertise_callback,
+                                                 advertise_data,
+                                                 advertise_settings)
         try:
-            self.adv_ad.ed.pop_event(
-                adv_succ.format(advertise_callback), self.default_timeout)
+            self.adv_ad.ed.pop_event(adv_succ.format(advertise_callback),
+                                     self.default_timeout)
         except Empty as error:
             self.log.error("Test failed with Empty error: {}".format(error))
             test_result = False
@@ -561,11 +563,12 @@
         test_result = reset_bluetooth([self.android_devices[1]])
         if not test_result:
             return test_result
-        self.adv_ad.droid.bleStartBleAdvertising(
-            advertise_callback, advertise_data, advertise_settings)
+        self.adv_ad.droid.bleStartBleAdvertising(advertise_callback,
+                                                 advertise_data,
+                                                 advertise_settings)
         try:
-            self.adv_ad.ed.pop_event(
-                adv_succ.format(advertise_callback), self.default_timeout)
+            self.adv_ad.ed.pop_event(adv_succ.format(advertise_callback),
+                                     self.default_timeout)
         except Empty as error:
             self.log.error("Test failed with Empty error: {}".format(error))
             test_result = False
@@ -610,19 +613,20 @@
             self.adv_ad.droid.bleSetAdvertiseSettingsTimeout(
                 advertise_timeout_s * 1000)
 
-            self.adv_ad.droid.bleStartBleAdvertising(
-                advertise_callback, advertise_data, advertise_settings)
+            self.adv_ad.droid.bleStartBleAdvertising(advertise_callback,
+                                                     advertise_data,
+                                                     advertise_settings)
             try:
-                self.adv_ad.ed.pop_event(
-                    adv_succ.format(advertise_callback), self.default_timeout)
+                self.adv_ad.ed.pop_event(adv_succ.format(advertise_callback),
+                                         self.default_timeout)
             except Empty as error:
-                self.log.error("Test failed with Empty error: {}".format(
-                    error))
+                self.log.error(
+                    "Test failed with Empty error: {}".format(error))
                 test_result = False
             except concurrent.futures._base.TimeoutError as error:
                 self.log.debug(
-                    "Test failed, filtering callback onSuccess never occurred: {}".
-                    format(error))
+                    "Test failed, filtering callback onSuccess never occurred: {}"
+                    .format(error))
 
             if not test_result:
                 return test_result
diff --git a/acts_tests/tests/google/ble/concurrency/ConcurrentGattConnectTest.py b/acts_tests/tests/google/ble/concurrency/ConcurrentGattConnectTest.py
index 4162b23..d5f8f01 100644
--- a/acts_tests/tests/google/ble/concurrency/ConcurrentGattConnectTest.py
+++ b/acts_tests/tests/google/ble/concurrency/ConcurrentGattConnectTest.py
@@ -21,7 +21,6 @@
 
 from queue import Empty
 import concurrent.futures
-import threading
 import time
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.bt_constants import ble_scan_settings_modes
@@ -83,7 +82,6 @@
         super(BluetoothBaseTest, self).setup_class()
         self.pri_dut = self.android_devices[0]
 
-
         # Create 5 advertisements from different android devices
         for i in range(1, self.max_connections + 1):
             # Set device name
diff --git a/acts_tests/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py b/acts_tests/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
index 70dcb89..9f81675 100644
--- a/acts_tests/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
+++ b/acts_tests/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
@@ -18,11 +18,9 @@
 2 connections test cases. This test was designed to be run in a shield box.
 """
 
-import threading
 import time
 
 from acts import utils
-from queue import Empty
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
@@ -65,7 +63,8 @@
     # The formula is that the min/max ce time should be less than half the connection
     # interval and must be multiples of the le_connection_event_time_step.
     def _calc_min_max_ce_time(self, le_connection_interval):
-        conn_event_time_steps = int((le_connection_interval/2)/le_connection_event_time_step_ms)
+        conn_event_time_steps = int(
+            (le_connection_interval / 2) / le_connection_event_time_step_ms)
         conn_event_time_steps -= 1
         return (le_connection_event_time_step_ms * conn_event_time_steps)
 
@@ -94,27 +93,30 @@
 
         self.log.info(
             "_run_coc_connection_throughput_2_conn: is_secured={}, Interval={}, buffer_size={}, "
-            "le_tx_data_length={}, min_ce_len={}".format(is_secured, le_connection_interval,
-                                          buffer_size, le_tx_data_length, min_ce_len))
+            "le_tx_data_length={}, min_ce_len={}".format(
+                is_secured, le_connection_interval, buffer_size,
+                le_tx_data_length, min_ce_len))
         status, client_conn_id1, server_conn_id1 = orchestrate_coc_connection(
             self.client_ad, self.server_ad, True, is_secured,
-            le_connection_interval, le_tx_data_length, default_bluetooth_socket_timeout_ms,
-            min_ce_len, max_ce_len)
+            le_connection_interval, le_tx_data_length,
+            default_bluetooth_socket_timeout_ms, min_ce_len, max_ce_len)
         if not status:
             return False
 
         status, client_conn_id2, server_conn_id2 = orchestrate_coc_connection(
             self.client_ad, self.server2_ad, True, is_secured,
-            le_connection_interval, le_tx_data_length, default_bluetooth_socket_timeout_ms,
-            min_ce_len, max_ce_len)
+            le_connection_interval, le_tx_data_length,
+            default_bluetooth_socket_timeout_ms, min_ce_len, max_ce_len)
         if not status:
             return False
 
         list_server_ad = [self.server_ad, self.server2_ad]
         list_client_conn_id = [client_conn_id1, client_conn_id2]
-        data_rate = do_multi_connection_throughput(
-            self.client_ad, list_server_ad, list_client_conn_id,
-            num_iterations, number_buffers, buffer_size)
+        data_rate = do_multi_connection_throughput(self.client_ad,
+                                                   list_server_ad,
+                                                   list_client_conn_id,
+                                                   num_iterations,
+                                                   number_buffers, buffer_size)
         if data_rate <= 0:
             return False
 
diff --git a/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py b/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py
index 9e95dcb..1e9aafb 100644
--- a/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py
+++ b/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py
@@ -18,11 +18,9 @@
 test cases. This test was designed to be run in a shield box.
 """
 
-import threading
 import time
 
 from acts import utils
-from queue import Empty
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
@@ -93,9 +91,11 @@
 
         list_server_ad = [self.server_ad]
         list_client_conn_id = [client_conn_id]
-        data_rate = do_multi_connection_throughput(
-            self.client_ad, list_server_ad, list_client_conn_id,
-            num_iterations, number_buffers, buffer_size)
+        data_rate = do_multi_connection_throughput(self.client_ad,
+                                                   list_server_ad,
+                                                   list_client_conn_id,
+                                                   num_iterations,
+                                                   number_buffers, buffer_size)
         if data_rate <= 0:
             return False
         self.log.info(
@@ -355,8 +355,9 @@
         le_connection_interval = 10
         buffer_size = 60
         le_tx_data_length = buffer_size + l2cap_coc_header_size
-        return self._run_coc_connection_throughput(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+        return self._run_coc_connection_throughput(is_secured, buffer_size,
+                                                   le_connection_interval,
+                                                   le_tx_data_length)
 
     @BluetoothBaseTest.bt_test_wrap
     @test_tracker_info(uuid='c32dac07-623a-4fdd-96c6-387a76afb2af')
@@ -393,8 +394,9 @@
         le_connection_interval = 10
         buffer_size = 80
         le_tx_data_length = buffer_size + l2cap_coc_header_size
-        return self._run_coc_connection_throughput(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+        return self._run_coc_connection_throughput(is_secured, buffer_size,
+                                                   le_connection_interval,
+                                                   le_tx_data_length)
 
     @BluetoothBaseTest.bt_test_wrap
     @test_tracker_info(uuid='45d1b0c1-73b6-483f-ac6b-c3cec805da32')
@@ -431,8 +433,9 @@
         le_connection_interval = 10
         buffer_size = 120
         le_tx_data_length = buffer_size + l2cap_coc_header_size
-        return self._run_coc_connection_throughput(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+        return self._run_coc_connection_throughput(is_secured, buffer_size,
+                                                   le_connection_interval,
+                                                   le_tx_data_length)
 
     @BluetoothBaseTest.bt_test_wrap
     @test_tracker_info(uuid='85f07f07-1017-42db-b38d-df0bf2fce804')
@@ -469,8 +472,9 @@
         le_connection_interval = 15
         buffer_size = 120
         le_tx_data_length = buffer_size + l2cap_coc_header_size
-        return self._run_coc_connection_throughput(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+        return self._run_coc_connection_throughput(is_secured, buffer_size,
+                                                   le_connection_interval,
+                                                   le_tx_data_length)
 
     @BluetoothBaseTest.bt_test_wrap
     @test_tracker_info(uuid='4d3d4a06-7bbb-4a8c-9016-f326560cebb0')
@@ -507,8 +511,9 @@
         le_connection_interval = 15
         buffer_size = 180
         le_tx_data_length = buffer_size + l2cap_coc_header_size
-        return self._run_coc_connection_throughput(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+        return self._run_coc_connection_throughput(is_secured, buffer_size,
+                                                   le_connection_interval,
+                                                   le_tx_data_length)
 
     @BluetoothBaseTest.bt_test_wrap
     @test_tracker_info(uuid='124d85ba-41e6-4ab7-a017-99a88db7524a')
@@ -545,8 +550,9 @@
         le_connection_interval = 20
         buffer_size = 240
         le_tx_data_length = buffer_size + l2cap_coc_header_size
-        return self._run_coc_connection_throughput(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+        return self._run_coc_connection_throughput(is_secured, buffer_size,
+                                                   le_connection_interval,
+                                                   le_tx_data_length)
 
     @BluetoothBaseTest.bt_test_wrap
     @test_tracker_info(uuid='218932bc-ebb0-4c2b-96ad-220c600b50b1')
@@ -583,5 +589,6 @@
         le_connection_interval = 30
         buffer_size = 240
         le_tx_data_length = buffer_size + l2cap_coc_header_size
-        return self._run_coc_connection_throughput(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+        return self._run_coc_connection_throughput(is_secured, buffer_size,
+                                                   le_connection_interval,
+                                                   le_tx_data_length)
diff --git a/acts_tests/tests/google/ble/filtering/FilteringTest.py b/acts_tests/tests/google/ble/filtering/FilteringTest.py
index 0e898c8..8c00b2f 100644
--- a/acts_tests/tests/google/ble/filtering/FilteringTest.py
+++ b/acts_tests/tests/google/ble/filtering/FilteringTest.py
@@ -16,7 +16,6 @@
 
 import itertools as it
 import pprint
-import time
 
 from queue import Empty
 from acts.test_decorators import test_tracker_info
@@ -11874,22 +11873,22 @@
             self.log.error("Expected callback type: {}, Found callback type: "
                            "{}".format(self.default_callback, callback_type))
             test_result = False
-        if 'include_device_name' in filters.keys() and filters[
-                'include_device_name'] is not False:
+        if 'include_device_name' in filters.keys(
+        ) and filters['include_device_name'] is not False:
             if event['data']['Result']['deviceName'] != filters[
                     'include_device_name']:
                 self.log.error(
-                    "Expected device name: {}, Found device name: {}"
-                    .format(filters['include_device_name'], event['data'][
-                        'Result']['deviceName']))
+                    "Expected device name: {}, Found device name: {}".format(
+                        filters['include_device_name'],
+                        event['data']['Result']['deviceName']))
 
                 test_result = False
         elif 'deviceName' in event['data']['Result'].keys():
             self.log.error(
                 "Device name was found when it wasn't meant to be included.")
             test_result = False
-        if ('include_tx_power_level' in filters.keys() and
-                filters['include_tx_power_level'] is not False):
+        if ('include_tx_power_level' in filters.keys()
+                and filters['include_tx_power_level'] is not False):
             if not event['data']['Result']['txPowerLevel']:
                 self.log.error(
                     "Expected to find tx power level in event but found none.")
@@ -11908,22 +11907,25 @@
         if 'is_connectable' in settings_in_effect.keys():
             if (event['data']['SettingsInEffect']['isConnectable'] !=
                     settings_in_effect['is_connectable']):
-                self.log.error("Expected is connectable value: {}, Actual is "
-                               "connectable value:".format(settings_in_effect[
-                                   'is_connectable'], event['data'][
-                                       'SettingsInEffect']['isConnectable']))
+                self.log.error(
+                    "Expected is connectable value: {}, Actual is "
+                    "connectable value:".format(
+                        settings_in_effect['is_connectable'],
+                        event['data']['SettingsInEffect']['isConnectable']))
                 test_result = False
         elif (event['data']['SettingsInEffect']['isConnectable'] !=
               self.default_is_connectable):
             self.log.error(
-                "Default value for isConnectable did not match what was found.")
+                "Default value for isConnectable did not match what was found."
+            )
             test_result = False
         if 'mode' in settings_in_effect.keys():
             if (event['data']['SettingsInEffect']['mode'] !=
                     settings_in_effect['mode']):
-                self.log.error("Expected mode value: {}, Actual mode value: {}"
-                               .format(settings_in_effect['mode'], event[
-                                   'data']['SettingsInEffect']['mode']))
+                self.log.error(
+                    "Expected mode value: {}, Actual mode value: {}".format(
+                        settings_in_effect['mode'],
+                        event['data']['SettingsInEffect']['mode']))
                 test_result = False
         elif (event['data']['SettingsInEffect']['mode'] !=
               self.default_advertise_mode):
@@ -11958,8 +11960,8 @@
             self.adv_ad.droid.bleSetAdvertiseSettingsIsConnectable(
                 settings_in_effect['is_connectable'])
         if 'mode' in settings_in_effect.keys():
-            self.log.debug("Setting advertisement mode to {}"
-                           .format(settings_in_effect['mode']))
+            self.log.debug("Setting advertisement mode to {}".format(
+                settings_in_effect['mode']))
             self.adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
                 settings_in_effect['mode'])
         if 'tx_power_level' in settings_in_effect.keys():
@@ -11968,24 +11970,25 @@
             self.adv_ad.droid.bleSetAdvertiseSettingsTxPowerLevel(
                 settings_in_effect['tx_power_level'])
         filter_list = self.scn_ad.droid.bleGenFilterList()
-        if ('include_device_name' in filters.keys() and
-                filters['include_device_name'] is not False):
+        if ('include_device_name' in filters.keys()
+                and filters['include_device_name'] is not False):
 
-            self.log.debug("Setting advertisement include_device_name to {}"
-                           .format(filters['include_device_name']))
+            self.log.debug(
+                "Setting advertisement include_device_name to {}".format(
+                    filters['include_device_name']))
             self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
             filters['include_device_name'] = (
                 self.adv_ad.droid.bluetoothGetLocalName())
             self.log.debug("Setting scanner include_device_name to {}".format(
                 filters['include_device_name']))
-            self.scn_ad.droid.bleSetScanFilterDeviceName(filters[
-                'include_device_name'])
+            self.scn_ad.droid.bleSetScanFilterDeviceName(
+                filters['include_device_name'])
         else:
             self.log.debug(
                 "Setting advertisement include_device_name to False")
             self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(False)
-        if ('include_tx_power_level' in filters.keys() and
-                filters['include_tx_power_level'] is not False):
+        if ('include_tx_power_level' in filters.keys()
+                and filters['include_tx_power_level'] is not False):
             self.log.debug(
                 "Setting advertisement include_tx_power_level to True")
             self.adv_ad.droid.bleSetAdvertiseDataIncludeTxPowerLevel(True)
@@ -12013,26 +12016,26 @@
         if 'manufacturer_specific_data_list' in filters.keys():
             for pair in filters['manufacturer_specific_data_list']:
                 (manu_id, manu_data) = pair
-                self.adv_ad.droid.bleAddAdvertiseDataManufacturerId(manu_id,
-                                                                    manu_data)
+                self.adv_ad.droid.bleAddAdvertiseDataManufacturerId(
+                    manu_id, manu_data)
         if 'service_mask' in filters.keys():
             self.scn_ad.droid.bleSetScanFilterServiceUuid(
                 filters['service_uuid'].upper(), filters['service_mask'])
             self.adv_ad.droid.bleSetAdvertiseDataSetServiceUuids(
                 [filters['service_uuid'].upper()])
         elif 'service_uuid' in filters.keys():
-            self.scn_ad.droid.bleSetScanFilterServiceUuid(filters[
-                'service_uuid'])
+            self.scn_ad.droid.bleSetScanFilterServiceUuid(
+                filters['service_uuid'])
             self.adv_ad.droid.bleSetAdvertiseDataSetServiceUuids(
                 [filters['service_uuid']])
         self.scn_ad.droid.bleBuildScanFilter(filter_list)
         advertise_callback, advertise_data, advertise_settings = (
             generate_ble_advertise_objects(self.adv_ad.droid))
-        if ('scan_mode' in settings_in_effect and
-                settings_in_effect['scan_mode'] !=
+        if ('scan_mode' in settings_in_effect
+                and settings_in_effect['scan_mode'] !=
                 ble_scan_settings_modes['opportunistic']):
-            self.scn_ad.droid.bleSetScanSettingsScanMode(settings_in_effect[
-                'scan_mode'])
+            self.scn_ad.droid.bleSetScanSettingsScanMode(
+                settings_in_effect['scan_mode'])
         else:
             self.scn_ad.droid.bleSetScanSettingsScanMode(
                 ble_scan_settings_modes['low_latency'])
@@ -12042,8 +12045,8 @@
                                           scan_callback)
         opportunistic = False
         scan_settings2, scan_callback2 = None, None
-        if ('scan_mode' in settings_in_effect and
-                settings_in_effect['scan_mode'] ==
+        if ('scan_mode' in settings_in_effect
+                and settings_in_effect['scan_mode'] ==
                 ble_scan_settings_modes['opportunistic']):
             opportunistic = True
             scan_settings2 = self.scn_ad.droid.bleBuildScanSetting()
@@ -12052,8 +12055,9 @@
                                               scan_callback2)
             self.scn_ad.droid.bleSetScanSettingsScanMode(
                 ble_scan_settings_modes['opportunistic'])
-        self.adv_ad.droid.bleStartBleAdvertising(
-            advertise_callback, advertise_data, advertise_settings)
+        self.adv_ad.droid.bleStartBleAdvertising(advertise_callback,
+                                                 advertise_data,
+                                                 advertise_settings)
         regex = "(" + adv_succ.format(
             advertise_callback) + "|" + adv_fail.format(
                 advertise_callback) + ")"
@@ -12078,8 +12082,8 @@
             event = self.scn_ad.ed.pop_event(expected_scan_event_name,
                                              self.default_timeout)
         except Empty:
-            self.log.error("Scan event not found: {}".format(
-                expected_scan_event_name))
+            self.log.error(
+                "Scan event not found: {}".format(expected_scan_event_name))
             return False
         if not self._blescan_verify_onscanresult_event(event, filters):
             return False
diff --git a/acts_tests/tests/google/ble/gatt/GattConnectTest.py b/acts_tests/tests/google/ble/gatt/GattConnectTest.py
index ebe5714..7e9df02 100644
--- a/acts_tests/tests/google/ble/gatt/GattConnectTest.py
+++ b/acts_tests/tests/google/ble/gatt/GattConnectTest.py
@@ -17,7 +17,6 @@
 This test script exercises different GATT connection tests.
 """
 
-import pprint
 from queue import Empty
 import time
 
@@ -112,8 +111,9 @@
             return False
         return True
 
-    def _verify_mtu_changed_on_client_and_server(
-            self, expected_mtu, gatt_callback, gatt_server_callback):
+    def _verify_mtu_changed_on_client_and_server(self, expected_mtu,
+                                                 gatt_callback,
+                                                 gatt_server_callback):
         expected_event = gatt_cb_strings['mtu_changed'].format(gatt_callback)
         try:
             mtu_event = self.cen_ad.ed.pop_event(expected_event,
@@ -1069,8 +1069,9 @@
         advertise_callback = self.per_ad.droid.bleGenBleAdvertiseCallback()
 
         # Step 1: Start advertisement
-        self.per_ad.droid.bleStartBleAdvertising(
-            advertise_callback, advertise_data, advertise_settings)
+        self.per_ad.droid.bleStartBleAdvertising(advertise_callback,
+                                                 advertise_data,
+                                                 advertise_settings)
 
         # Setup scan settings for low_latency scanning and to include the local name
         # of the advertisement started in step 1.
@@ -1094,8 +1095,8 @@
         expected_event_name = scan_result.format(scan_callback)
         try:
             mac_address_pre_restart = self.cen_ad.ed.pop_event(
-                expected_event_name, self.default_timeout)['data']['Result'][
-                    'deviceInfo']['address']
+                expected_event_name, self.default_timeout
+            )['data']['Result']['deviceInfo']['address']
             self.log.info(
                 "Peripheral advertisement found with mac address: {}".format(
                     mac_address_pre_restart))
@@ -1106,24 +1107,25 @@
         # Step 3: Restart peripheral advertising such that a new mac address is
         # created.
         self.per_ad.droid.bleStopBleAdvertising(advertise_callback)
-        self.per_ad.droid.bleStartBleAdvertising(
-            advertise_callback, advertise_data, advertise_settings)
+        self.per_ad.droid.bleStartBleAdvertising(advertise_callback,
+                                                 advertise_data,
+                                                 advertise_settings)
 
         mac_address_post_restart = mac_address_pre_restart
 
         while True:
             try:
                 mac_address_post_restart = self.cen_ad.ed.pop_event(
-                    expected_event_name, self.default_timeout)['data']['Result'][
-                        'deviceInfo']['address']
+                    expected_event_name, self.default_timeout
+                )['data']['Result']['deviceInfo']['address']
                 self.log.info(
-                    "Peripheral advertisement found with mac address: {}".format(
-                        mac_address_post_restart))
+                    "Peripheral advertisement found with mac address: {}".
+                    format(mac_address_post_restart))
             except Empty:
                 self.log.info("Peripheral advertisement not found")
                 test_result = False
 
-            if  mac_address_pre_restart != mac_address_post_restart:
+            if mac_address_pre_restart != mac_address_post_restart:
                 break
 
         self.cen_ad.droid.bleStopBleScan(scan_callback)
diff --git a/acts_tests/tests/google/ble/gatt/GattNotifyTest.py b/acts_tests/tests/google/ble/gatt/GattNotifyTest.py
index 4b142a5..6962968 100644
--- a/acts_tests/tests/google/ble/gatt/GattNotifyTest.py
+++ b/acts_tests/tests/google/ble/gatt/GattNotifyTest.py
@@ -25,7 +25,6 @@
 from acts_contrib.test_utils.bt.bt_constants import gatt_event
 from acts_contrib.test_utils.bt.bt_constants import gatt_cb_strings
 from acts_contrib.test_utils.bt.bt_constants import gatt_char_desc_uuids
-from math import ceil
 
 
 class GattNotifyTest(GattConnectedBaseTest):
@@ -76,8 +75,9 @@
         bt_device_id = 0
         status = 0
         #confirm notification registration was successful
-        self.per_ad.droid.gattServerSendResponse(
-            self.gatt_server, bt_device_id, request_id, status, 0, [])
+        self.per_ad.droid.gattServerSendResponse(self.gatt_server,
+                                                 bt_device_id, request_id,
+                                                 status, 0, [])
         #wait for client to get response
         event = self._client_wait(gatt_event['desc_write'])
 
diff --git a/acts_tests/tests/google/ble/gatt/GattToolTest.py b/acts_tests/tests/google/ble/gatt/GattToolTest.py
index 116495c..3982ab0 100644
--- a/acts_tests/tests/google/ble/gatt/GattToolTest.py
+++ b/acts_tests/tests/google/ble/gatt/GattToolTest.py
@@ -23,7 +23,6 @@
 "notifiable_char_uuid"
 """
 
-import pprint
 from queue import Empty
 import time
 
@@ -420,9 +419,11 @@
             self.NOTIFIABLE_CHAR_UUID, self.CCC_DESC_UUID,
             gatt_descriptor['enable_notification_value'])
 
-        self.cen_ad.droid.gattClientWriteDescriptor(
-            bluetooth_gatt, discovered_services_index, test_service_index,
-            self.NOTIFIABLE_CHAR_UUID, self.CCC_DESC_UUID)
+        self.cen_ad.droid.gattClientWriteDescriptor(bluetooth_gatt,
+                                                    discovered_services_index,
+                                                    test_service_index,
+                                                    self.NOTIFIABLE_CHAR_UUID,
+                                                    self.CCC_DESC_UUID)
 
         self.cen_ad.droid.gattClientSetCharacteristicNotification(
             bluetooth_gatt, discovered_services_index, test_service_index,
diff --git a/acts_tests/tests/google/ble/scan/BleScanScreenStateTest.py b/acts_tests/tests/google/ble/scan/BleScanScreenStateTest.py
index a19e80c..85f95b8 100644
--- a/acts_tests/tests/google/ble/scan/BleScanScreenStateTest.py
+++ b/acts_tests/tests/google/ble/scan/BleScanScreenStateTest.py
@@ -17,9 +17,6 @@
 This test script exercises different scan filters with different screen states.
 """
 
-import concurrent
-import json
-import pprint
 import time
 
 from queue import Empty
@@ -56,8 +53,9 @@
             ble_advertise_settings_modes['low_latency'])
         self.advertise_callback, advertise_data, advertise_settings = (
             generate_ble_advertise_objects(self.adv_ad.droid))
-        self.adv_ad.droid.bleStartBleAdvertising(
-            self.advertise_callback, advertise_data, advertise_settings)
+        self.adv_ad.droid.bleStartBleAdvertising(self.advertise_callback,
+                                                 advertise_data,
+                                                 advertise_settings)
         try:
             self.adv_ad.ed.pop_event(adv_succ.format(self.advertise_callback))
         except Empty:
@@ -73,8 +71,8 @@
 
     def _scan_found_results(self):
         try:
-            self.scn_ad.ed.pop_event(
-                scan_result.format(self.scan_callback), bt_default_timeout)
+            self.scn_ad.ed.pop_event(scan_result.format(self.scan_callback),
+                                     bt_default_timeout)
             self.log.info("Found an advertisement.")
         except Empty:
             self.log.info("Did not find an advertisement.")
@@ -432,8 +430,8 @@
         time.sleep(2)
 
         # Step 3
-        self.scn_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
-            'opportunistic'])
+        self.scn_ad.droid.bleSetScanSettingsScanMode(
+            ble_scan_settings_modes['opportunistic'])
         filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
             self.scn_ad.droid)
         self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
@@ -447,15 +445,15 @@
 
         # Step 5
         try:
-            self.scn_ad.ed.pop_event(
-                scan_result.format(scan_callback), self.shorter_scan_timeout)
+            self.scn_ad.ed.pop_event(scan_result.format(scan_callback),
+                                     self.shorter_scan_timeout)
             self.log.error("Found an advertisement on opportunistic scan.")
             return False
         except Empty:
             self.log.info("Did not find an advertisement.")
         try:
-            self.scn_ad.ed.pop_event(
-                scan_result.format(scan_callback2), self.shorter_scan_timeout)
+            self.scn_ad.ed.pop_event(scan_result.format(scan_callback2),
+                                     self.shorter_scan_timeout)
             self.log.error("Found an advertisement on scan instance.")
             return False
         except Empty:
@@ -466,16 +464,16 @@
 
         # Step 7
         try:
-            self.scn_ad.ed.pop_event(
-                scan_result.format(scan_callback), self.shorter_scan_timeout)
+            self.scn_ad.ed.pop_event(scan_result.format(scan_callback),
+                                     self.shorter_scan_timeout)
             self.log.info("Found an advertisement on opportunistic scan.")
         except Empty:
             self.log.error(
                 "Did not find an advertisement on opportunistic scan.")
             return False
         try:
-            self.scn_ad.ed.pop_event(
-                scan_result.format(scan_callback2), self.shorter_scan_timeout)
+            self.scn_ad.ed.pop_event(scan_result.format(scan_callback2),
+                                     self.shorter_scan_timeout)
             self.log.info("Found an advertisement on scan instance.")
         except Empty:
             self.log.info("Did not find an advertisement.")
@@ -545,8 +543,8 @@
         # Step 5
         for callback in scan_callback_list:
             try:
-                self.scn_ad.ed.pop_event(
-                    scan_result.format(callback), self.shorter_scan_timeout)
+                self.scn_ad.ed.pop_event(scan_result.format(callback),
+                                         self.shorter_scan_timeout)
                 self.log.info("Found an advertisement.")
             except Empty:
                 self.log.info("Did not find an advertisement.")
diff --git a/acts_tests/tests/google/bt/BtAirplaneModeTest.py b/acts_tests/tests/google/bt/BtAirplaneModeTest.py
index aeafc7a..fac9641 100644
--- a/acts_tests/tests/google/bt/BtAirplaneModeTest.py
+++ b/acts_tests/tests/google/bt/BtAirplaneModeTest.py
@@ -22,7 +22,6 @@
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
 from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
-from queue import Empty
 import time
 
 
diff --git a/acts_tests/tests/google/bt/BtBasicFunctionalityTest.py b/acts_tests/tests/google/bt/BtBasicFunctionalityTest.py
index ade4c0a..3ee7f0a 100644
--- a/acts_tests/tests/google/bt/BtBasicFunctionalityTest.py
+++ b/acts_tests/tests/google/bt/BtBasicFunctionalityTest.py
@@ -20,7 +20,6 @@
 
 import time
 
-from queue import Empty
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.bt_constants import bt_scan_mode_types
diff --git a/acts_tests/tests/google/bt/RfcommTest.py b/acts_tests/tests/google/bt/RfcommTest.py
index 14dc7bb..a551fab 100644
--- a/acts_tests/tests/google/bt/RfcommTest.py
+++ b/acts_tests/tests/google/bt/RfcommTest.py
@@ -18,10 +18,8 @@
 This test was designed to be run in a shield box.
 """
 
-import threading
 import time
 
-from queue import Empty
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.bt_constants import bt_rfcomm_uuids
@@ -60,8 +58,9 @@
         return True
 
     def teardown_test(self):
-        if verify_server_and_client_connected(
-                self.client_ad, self.server_ad, log=False):
+        if verify_server_and_client_connected(self.client_ad,
+                                              self.server_ad,
+                                              log=False):
             self.client_ad.droid.bluetoothRfcommStop()
             self.server_ad.droid.bluetoothRfcommStop()
 
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py b/acts_tests/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py
index b8d5ec8..7ecd910 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarBasicFunctionalityTest.py
@@ -17,9 +17,6 @@
 Test script to execute Bluetooth basic functionality test cases relevant to car.
 """
 
-import time
-
-from queue import Empty
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.BtEnum import BluetoothScanModeType
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarHfpConnectionTest.py b/acts_tests/tests/google/bt/car_bt/BtCarHfpConnectionTest.py
index e0ea79e..be7bcd3 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarHfpConnectionTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarHfpConnectionTest.py
@@ -17,8 +17,6 @@
 Test the HFP profile for calling and connection management.
 """
 
-import time
-
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarMultiUserTest.py b/acts_tests/tests/google/bt/car_bt/BtCarMultiUserTest.py
index 6ec1320..6098e41 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarMultiUserTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarMultiUserTest.py
@@ -23,7 +23,6 @@
 from acts_contrib.test_utils.users import users
 import time
 import random
-import re
 
 
 class BtCarMultiUserTest(BluetoothBaseTest):
@@ -73,7 +72,8 @@
             Fail if bluetooth crashed once
         """
         laps = 10
-        initial_bt_crashes = bt_test_utils.get_bluetooth_crash_count(self.droid_ad)
+        initial_bt_crashes = bt_test_utils.get_bluetooth_crash_count(
+            self.droid_ad)
         crashes_since_start = initial_bt_crashes
 
         for count in range(laps):
@@ -89,10 +89,15 @@
             if not users.switch_user(self.droid_ad, self.userid_2):
                 return False
 
-            crashes_since_start = abs(initial_bt_crashes - bt_test_utils.get_bluetooth_crash_count(self.droid_ad))
-            self.log.info("Current bluetooth crashes: {} over {} laps".format(crashes_since_start, count))
+            crashes_since_start = abs(
+                initial_bt_crashes -
+                bt_test_utils.get_bluetooth_crash_count(self.droid_ad))
+            self.log.info("Current bluetooth crashes: {} over {} laps".format(
+                crashes_since_start, count))
 
         if crashes_since_start != 0:
-            self.log.error("Bluetooth stack crashed {} times over {} laps".format(crashes_since_start, laps))
+            self.log.error(
+                "Bluetooth stack crashed {} times over {} laps".format(
+                    crashes_since_start, laps))
             return False
         return True
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py b/acts_tests/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py
index 82751af..622ecb6 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarPairedConnectDisconnectTest.py
@@ -25,8 +25,6 @@
       2.1 Check that devices are disconnected.
 """
 
-import time
-
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.base_test import BaseTestClass
@@ -46,8 +44,9 @@
 
         # Pair the devices.
         # This call may block until some specified timeout in bt_test_utils.py.
-        result = bt_test_utils.pair_pri_to_sec(
-            self.car, self.ph, auto_confirm=False)
+        result = bt_test_utils.pair_pri_to_sec(self.car,
+                                               self.ph,
+                                               auto_confirm=False)
 
         asserts.assert_true(result, "pair_pri_to_sec returned false.")
 
@@ -114,8 +113,8 @@
         failure = 0
         addr = self.ph.droid.bluetoothGetLocalAddress()
         for i in range(NUM_TEST_RUNS):
-            self.log.info("Running test [" + str(i) + "/" + str(NUM_TEST_RUNS)
-                          + "]")
+            self.log.info("Running test [" + str(i) + "/" +
+                          str(NUM_TEST_RUNS) + "]")
             success = bt_test_utils.connect_pri_to_sec(
                 self.car, self.ph,
                 set([
@@ -126,10 +125,10 @@
             # Check if we got connected.
             if not success:
                 self.car.log.info("Not all profiles connected.")
-                if (bt_test_utils.is_hfp_client_device_connected(self.car,
-                                                                 addr) and
-                        bt_test_utils.is_a2dp_snk_device_connected(self.car,
-                                                                   addr)):
+                if (bt_test_utils.is_hfp_client_device_connected(
+                        self.car, addr)
+                        and bt_test_utils.is_a2dp_snk_device_connected(
+                            self.car, addr)):
                     self.car.log.info(
                         "HFP Client or A2DP SRC connected successfully.")
                 else:
@@ -145,17 +144,17 @@
 
             if success is False:
                 self.car.log.info("Disconnect failed.")
-                if (bt_test_utils.is_hfp_client_device_connected(self.car,
-                                                                 addr) or
-                        bt_test_utils.is_a2dp_snk_device_connected(self.car,
-                                                                   addr)):
+                if (bt_test_utils.is_hfp_client_device_connected(
+                        self.car, addr)
+                        or bt_test_utils.is_a2dp_snk_device_connected(
+                            self.car, addr)):
                     self.car.log.info(
                         "HFP Client or A2DP SRC failed to disconnect.")
                     failure = failure + 1
                 continue
 
-        self.log.info("Failure {} total tests {}".format(failure,
-                                                         NUM_TEST_RUNS))
+        self.log.info("Failure {} total tests {}".format(
+            failure, NUM_TEST_RUNS))
         if failure > 0:
             return False
         return True
diff --git a/acts_tests/tests/google/bt/car_bt/BtCarPbapTest.py b/acts_tests/tests/google/bt/car_bt/BtCarPbapTest.py
index a4d241f..4ced6ed 100644
--- a/acts_tests/tests/google/bt/car_bt/BtCarPbapTest.py
+++ b/acts_tests/tests/google/bt/car_bt/BtCarPbapTest.py
@@ -16,7 +16,6 @@
 """Test script to test PBAP contact download between two devices which can run SL4A.
 """
 
-import os
 import time
 
 from acts.test_decorators import test_tracker_info
@@ -56,7 +55,8 @@
         for device in self.android_devices:
             for permission in permissions_list:
                 device.adb.shell(
-                    "pm grant com.google.android.contacts {}".format(permission))
+                    "pm grant com.google.android.contacts {}".format(
+                        permission))
 
         # Pair the devices.
         # This call may block until some specified timeout in bt_test_utils.py.
@@ -113,7 +113,9 @@
 
     def erase_all_contacts(self):
         try:
-            return all(bt_contacts_utils.erase_contacts(device) for device in self.android_devices)
+            return all(
+                bt_contacts_utils.erase_contacts(device)
+                for device in self.android_devices)
         finally:
             # Allow all content providers to synchronize.
             time.sleep(1)
@@ -129,8 +131,8 @@
         bt_test_utils.connect_pri_to_sec(
             self.pce, self.pse,
             set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
-        bt_contacts_utils.wait_for_phone_number_update_complete(self.pce,
-                                                                count)
+        bt_contacts_utils.wait_for_phone_number_update_complete(
+            self.pce, count)
         contacts_added = self.verify_contacts_match()
         self.pce.droid.bluetoothPbapClientDisconnect(
             self.pse.droid.bluetoothGetLocalAddress())
@@ -381,8 +383,8 @@
         pse_call_log_count = self.pse.droid.callLogGetCount()
         self.log.info("Waiting for {} call logs to be transfered".format(
             pse_call_log_count))
-        bt_contacts_utils.wait_for_call_log_update_complete(self.pce,
-                                                            pse_call_log_count)
+        bt_contacts_utils.wait_for_call_log_update_complete(
+            self.pce, pse_call_log_count)
 
         if not bt_contacts_utils.get_and_compare_call_logs(
                 self.pse, self.pce, bt_contacts_utils.INCOMMING_CALL_TYPE):
@@ -427,14 +429,12 @@
 
         bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
                                                 PSE1_CONTACTS_FILE, 100)
-        bt_contacts_utils.import_device_contacts_from_vcf(self.pse,
-            self.contacts_destination_path,
-            PSE1_CONTACTS_FILE)
+        bt_contacts_utils.import_device_contacts_from_vcf(
+            self.pse, self.contacts_destination_path, PSE1_CONTACTS_FILE)
         bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
                                                 PSE2_CONTACTS_FILE, 100)
-        bt_contacts_utils.import_device_contacts_from_vcf(self.pse2,
-            self.contacts_destination_path,
-            PSE2_CONTACTS_FILE)
+        bt_contacts_utils.import_device_contacts_from_vcf(
+            self.pse2, self.contacts_destination_path, PSE2_CONTACTS_FILE)
 
         self.pce.droid.bluetoothPbapClientDisconnect(
             self.pse.droid.bluetoothGetLocalAddress())
@@ -458,8 +458,9 @@
         bt_contacts_utils.export_device_contacts_to_vcf(
             self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE)
 
-        merged_file = open('{}{}'.format(self.contacts_destination_path,
-                                         MERGED_CONTACTS_FILE), 'w')
+        merged_file = open(
+            '{}{}'.format(self.contacts_destination_path,
+                          MERGED_CONTACTS_FILE), 'w')
         for contacts_file in [PSE1_CONTACTS_FILE, PSE2_CONTACTS_FILE]:
             infile = open(self.contacts_destination_path + contacts_file)
             merged_file.write(infile.read())
@@ -483,4 +484,3 @@
         bt_contacts_utils.erase_contacts(self.pse)
         bt_contacts_utils.erase_contacts(self.pse2)
         return pse1_matches and pse2_matches and pse1andpse2_matches
-
diff --git a/acts_tests/tests/google/bt/gatt/GattOverBrEdrTest.py b/acts_tests/tests/google/bt/gatt/GattOverBrEdrTest.py
index 0d0ac0c..fff6232 100644
--- a/acts_tests/tests/google/bt/gatt/GattOverBrEdrTest.py
+++ b/acts_tests/tests/google/bt/gatt/GattOverBrEdrTest.py
@@ -17,7 +17,6 @@
 Test suite for GATT over BR/EDR.
 """
 
-import time
 from queue import Empty
 
 from acts.test_decorators import test_tracker_info
@@ -399,8 +398,8 @@
                 self.log.error(err)
                 return False
             self.log.info("Disconnecting from peripheral device.")
-            test_result = self._orchestrate_gatt_disconnection(bluetooth_gatt,
-                                                               gatt_callback)
+            test_result = self._orchestrate_gatt_disconnection(
+                bluetooth_gatt, gatt_callback)
             if not test_result:
                 self.log.info("Failed to disconnect from peripheral device.")
                 return False
@@ -492,9 +491,9 @@
                         self.cen_ad.droid.gattClientWriteDescriptor(
                             bluetooth_gatt, discovered_services_index, i,
                             characteristic, descriptor)
-                        event = self.per_ad.ed.pop_event(gatt_cb_strings[
-                            'desc_write_req'].format(gatt_server_callback),
-                                                         self.default_timeout)
+                        event = self.per_ad.ed.pop_event(
+                            gatt_cb_strings['desc_write_req'].format(
+                                gatt_server_callback), self.default_timeout)
                         self.log.info(
                             "onDescriptorWriteRequest event found: {}".format(
                                 event))
@@ -502,15 +501,15 @@
                         found_value = event['data']['value']
                         if found_value != test_value:
                             self.log.info("Values didn't match. Found: {}, "
-                                          "Expected: {}".format(found_value,
-                                                                test_value))
+                                          "Expected: {}".format(
+                                              found_value, test_value))
                             return False
                         self.per_ad.droid.gattServerSendResponse(
                             gatt_server, bt_device_id, request_id, status,
                             offset, test_value_return)
                         self.log.info(
                             "onDescriptorWrite event found: {}".format(
-                                self.cen_ad.ed.pop_event(gatt_cb_strings[
-                                    'desc_write'].format(
+                                self.cen_ad.ed.pop_event(
+                                    gatt_cb_strings['desc_write'].format(
                                         gatt_callback), self.default_timeout)))
         return True
diff --git a/acts_tests/tests/google/bt/headphone_automation/HeadphoneTest.py b/acts_tests/tests/google/bt/headphone_automation/HeadphoneTest.py
index 9d04088..874c2a9 100644
--- a/acts_tests/tests/google/bt/headphone_automation/HeadphoneTest.py
+++ b/acts_tests/tests/google/bt/headphone_automation/HeadphoneTest.py
@@ -20,10 +20,7 @@
 Shield box one: Android Device and 3 headset.
 """
 
-import json
-import os
 import time
-import sys
 
 from acts import logger
 from acts.asserts import assert_false
@@ -39,21 +36,18 @@
 from acts.controllers.relay_lib.sony_xb2_speaker import SonyXB2Speaker
 
 
-
 class HeadphoneTest(BluetoothBaseTest):
 
     WAIT_TIME = 10
     iterations = 10
 
-
     def _discover_and_pair(self, headphone):
         self.ad.droid.bluetoothStartDiscovery()
         time.sleep(10)
         self.ad.droid.bluetoothCancelDiscovery()
         for device in self.ad.droid.bluetoothGetDiscoveredDevices():
             if device['address'] == headphone.mac_address:
-                self.ad.droid.bluetoothDiscoverAndBond(
-                    headphone.mac_address)
+                self.ad.droid.bluetoothDiscoverAndBond(headphone.mac_address)
                 end_time = time.time() + 20
                 self.log.info("Found the mac address")
                 self.log.info("Verifying devices are bonded")
@@ -62,8 +56,8 @@
                     for d in bonded_devices:
                         if d['address'] == headphone.mac_address:
                             self.log.info("Successfully bonded to device.")
-                            self.log.info(
-                                'Bonded devices:\n%s', bonded_devices)
+                            self.log.info('Bonded devices:\n%s',
+                                          bonded_devices)
                             return True
         return False
 
@@ -84,8 +78,6 @@
         if hasattr(self, 'relay_devices'):
             for headphone in self.relay_devices:
                 headphone.setup()
-
-
         '''
         # Turn of the Power Supply for the headphones.
         if hasattr(self, 'relay_devices'):
@@ -134,7 +126,7 @@
               Priority: 0
         """
         for headphone in self.headphone_list:
-            self.log.info("Start testing on  " + headphone.name )
+            self.log.info("Start testing on  " + headphone.name)
 
             if self._discover_and_pair(headphone) is False:
                 # The device is probably not in pairing mode, put in pairing mode.
@@ -154,8 +146,6 @@
                 return False
             clear_bonded_devices(self.ad)
 
-
-
     @BluetoothBaseTest.bt_test_wrap
     @test_tracker_info(uuid='7ba73c39-2a69-4a72-b708-d603ce658740')
     def test_pair_unpair_stress(self):
@@ -228,6 +218,6 @@
                     msg = "Unable to pair to %s", headphone.name
                     assert_true(self._discover_and_pair(headphone), msg)
 
-            if len(self.ad.droid.bluetoothGetConnectedDevices()) != n+1:
+            if len(self.ad.droid.bluetoothGetConnectedDevices()) != n + 1:
                 self.log.error("Not connected to  %s", headphone.name)
                 return False
diff --git a/acts_tests/tests/google/bt/pan/BtPanTest.py b/acts_tests/tests/google/bt/pan/BtPanTest.py
index 75d14b2..ae011d3 100644
--- a/acts_tests/tests/google/bt/pan/BtPanTest.py
+++ b/acts_tests/tests/google/bt/pan/BtPanTest.py
@@ -27,8 +27,6 @@
 from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
 from acts_contrib.test_utils.bt.bt_test_utils import orchestrate_and_verify_pan_connection
 from acts_contrib.test_utils.tel.tel_test_utils import verify_http_connection
-from queue import Empty
-import time
 
 
 class BtPanTest(BluetoothBaseTest):
@@ -98,8 +96,8 @@
             self.log.error("Could not establish a PAN connection.")
             return False
         self.pan_dut.droid.bluetoothPanSetBluetoothTethering(False)
-        if not verify_http_connection(self.log, self.panu_dut,
-                                      expected_state=False):
+        if not verify_http_connection(
+                self.log, self.panu_dut, expected_state=False):
             self.log.error("PANU device still has internet access.")
             return False
         self.log.info("PANU device has no internet access as expected.")
diff --git a/acts_tests/tests/google/bt/performance/BtA2dpRangeTest.py b/acts_tests/tests/google/bt/performance/BtA2dpRangeTest.py
index c775dd7..3104452 100644
--- a/acts_tests/tests/google/bt/performance/BtA2dpRangeTest.py
+++ b/acts_tests/tests/google/bt/performance/BtA2dpRangeTest.py
@@ -21,20 +21,26 @@
 
 
 class BtA2dpRangeTest(A2dpBaseTest):
+
     def __init__(self, configs):
         super().__init__(configs)
         req_params = ['attenuation_vector', 'codecs']
+        opt_params = ['gain_mismatch', 'dual_chain']
         #'attenuation_vector' is a dict containing: start, stop and step of
         #attenuation changes
         #'codecs' is a list containing all codecs required in the tests
         self.unpack_userparams(req_params)
+        self.unpack_userparams(opt_params, dual_chain=None, gain_mismatch=None)
+
+    def setup_generated_tests(self):
         for codec_config in self.codecs:
-            self.generate_test_case(codec_config)
+            arg_set = [(codec_config, )]
+            self.generate_tests(test_logic=self.BtA2dp_test_logic,
+                                name_func=self.create_test_name,
+                                arg_sets=arg_set)
 
     def setup_class(self):
         super().setup_class()
-        opt_params = ['gain_mismatch', 'dual_chain']
-        self.unpack_userparams(opt_params, dual_chain=None, gain_mismatch=None)
         # Enable BQR on all android devices
         btutils.enable_bqr(self.android_devices)
         if hasattr(self, 'dual_chain') and self.dual_chain == 1:
@@ -49,14 +55,14 @@
             self.atten_c0.set_atten(INIT_ATTEN)
             self.atten_c1.set_atten(INIT_ATTEN)
 
-    def generate_test_case(self, codec_config):
-        def test_case_fn():
-            self.run_a2dp_to_max_range(codec_config)
+    def BtA2dp_test_logic(self, codec_config):
+        self.run_a2dp_to_max_range(codec_config)
 
+    def create_test_name(self, arg_set):
         if hasattr(self, 'dual_chain') and self.dual_chain == 1:
-            test_case_name = 'test_dual_bt_a2dp_range_codec_{}_gainmimatch_{}dB'.format(
-                codec_config['codec_type'], self.gain_mismatch)
+            test_case_name = 'test_dual_bt_a2dp_range_codec_{}_gainmismatch_{}dB'.format(
+                arg_set['codec_type'], self.gain_mismatch)
         else:
             test_case_name = 'test_bt_a2dp_range_codec_{}'.format(
-                codec_config['codec_type'])
-        setattr(self, test_case_name, test_case_fn)
+                arg_set['codec_type'])
+        return test_case_name
diff --git a/acts_tests/tests/google/bt/performance/BtA2dpRangeWithBleAdvTest.py b/acts_tests/tests/google/bt/performance/BtA2dpRangeWithBleAdvTest.py
index 5fe9503..16fedcd 100644
--- a/acts_tests/tests/google/bt/performance/BtA2dpRangeWithBleAdvTest.py
+++ b/acts_tests/tests/google/bt/performance/BtA2dpRangeWithBleAdvTest.py
@@ -57,35 +57,39 @@
           test_bt_a2dp_range_codec_SBC_adv_mode_low_latency_adv_tx_power_low
           test_bt_a2dp_range_codec_SBC_adv_mode_low_latency_adv_tx_power_medium
           test_bt_a2dp_range_codec_SBC_adv_mode_low_latency_adv_tx_power_high
-
       """
+
     def __init__(self, configs):
         super().__init__(configs)
         req_params = ['attenuation_vector', 'codecs']
-        #'attenuation_vector' is a dict containing: start, stop and step of
-        #attenuation changes
+        opt_params = ['gain_mismatch', 'dual_chain']
+        #'attenuation_vector' is a dict containing: start, stop and step of attenuation changes
         #'codecs' is a list containing all codecs required in the tests
+        #'gain_mismatch' is an offset value between the BT two chains
+        #'dual_chain' set to 1 enable sweeping attenuation for BT two chains
         self.unpack_userparams(req_params)
+        self.unpack_userparams(opt_params, dual_chian=None, gain_mismatch=None)
+
+    def setup_generated_tests(self):
         for codec_config in self.codecs:
-            # Loop all advertise modes and power levels
             for adv_mode in ble_advertise_settings_modes.items():
-                for adv_power_level in ble_advertise_settings_tx_powers.items(
-                ):
-                    self.generate_test_case(codec_config, adv_mode,
-                                            adv_power_level)
+                for adv_power_level in ble_advertise_settings_tx_powers.items():
+                    arg_set = [(codec_config, adv_mode, adv_power_level)]
+                    self.generate_tests(
+                        test_logic=self.BtA2dp_with_ble_adv_test_logic,
+                        name_func=self.create_test_name,
+                        arg_sets=arg_set)
 
     def setup_class(self):
         super().setup_class()
-        opt_params = ['gain_mismatch', 'dual_chain']
-        self.unpack_userparams(opt_params, dual_chain=None, gain_mismatch=None)
-        return setup_multiple_devices_for_bt_test(self.android_devices)
-        # Enable BQR on all android devices
+        #Enable BQR on all android devices
         btutils.enable_bqr(self.android_devices)
         if hasattr(self, 'dual_chain') and self.dual_chain == 1:
             self.atten_c0 = self.attenuators[0]
             self.atten_c1 = self.attenuators[1]
             self.atten_c0.set_atten(INIT_ATTEN)
             self.atten_c1.set_atten(INIT_ATTEN)
+        return setup_multiple_devices_for_bt_test(self.android_devices)
 
     def teardown_class(self):
         super().teardown_class()
@@ -93,20 +97,22 @@
             self.atten_c0.set_atten(INIT_ATTEN)
             self.atten_c1.set_atten(INIT_ATTEN)
 
-    def generate_test_case(self, codec_config, adv_mode, adv_power_level):
-        def test_case_fn():
-            adv_callback = self.start_ble_adv(adv_mode[1], adv_power_level[1])
-            self.run_a2dp_to_max_range(codec_config)
-            self.dut.droid.bleStopBleAdvertising(adv_callback)
-            self.log.info("Advertisement stopped Successfully")
+    def BtA2dp_with_ble_adv_test_logic(self, codec_config, adv_mode,
+                                       adv_power_level):
+        adv_callback = self.start_ble_adv(adv_mode[1], adv_power_level[1])
+        self.run_a2dp_to_max_range(codec_config)
+        self.dut.droid.bleStopBleAdvertising(adv_callback)
+        self.log.info("Advertisement stopped Successfully")
 
+    def create_test_name(self, codec_config, adv_mode, adv_power_level):
         if hasattr(self, 'dual_chain') and self.dual_chain == 1:
-            test_case_name = 'test_dual_bt_a2dp_range_codec_{}_gainmimatch_{}dB'.format(
-                codec_config['codec_type'], self.gain_mismatch)
+            test_case_name = 'test_dual_bt_a2dp_range_codec_{}_gainmismatch_{}dB_adv_mode_{}_adv_tx_power_{}'.format(
+                codec_config['codec_type'], self.gain_mismatch, adv_mode[0],
+                adv_power_level[0])
         else:
             test_case_name = 'test_bt_a2dp_range_codec_{}_adv_mode_{}_adv_tx_power_{}'.format(
                 codec_config['codec_type'], adv_mode[0], adv_power_level[0])
-        setattr(self, test_case_name, test_case_fn)
+        return test_case_name
 
     def start_ble_adv(self, adv_mode, adv_power_level):
         """Function to start an LE advertisement
@@ -140,3 +146,4 @@
             raise BtTestUtilsError(
                 "Advertiser did not start successfully {}".format(err))
         return advertise_callback
+
diff --git a/acts_tests/tests/google/bt/performance/BtA2dpRangeWithBleScanTest.py b/acts_tests/tests/google/bt/performance/BtA2dpRangeWithBleScanTest.py
index 6020c4a..4cd2dd7 100644
--- a/acts_tests/tests/google/bt/performance/BtA2dpRangeWithBleScanTest.py
+++ b/acts_tests/tests/google/bt/performance/BtA2dpRangeWithBleScanTest.py
@@ -25,32 +25,50 @@
 
 
 class BtA2dpRangeWithBleScanTest(A2dpBaseTest):
-    default_timeout = 10
+    """User can generate test case with below format.
+    test_bt_a2dp_range_codec_"Codec"_with_BLE_scan_"Scan Mode"
+
+    Below are the list of test cases:
+        test_bt_a2dp_range_codec_AAC_with_BLE_scan_balanced
+        test_bt_a2dp_range_codec_AAC_with_BLE_scan_low_latency
+        test_bt_a2dp_range_codec_AAC_with_BLE_scan_low_power
+        test_bt_a2dp_range_codec_AAC_with_BLE_scan_opportunistic
+        test_bt_a2dp_range_codec_SBC_with_BLE_scan_balanced
+        test_bt_a2dp_range_codec_SBC_with_BLE_scan_low_latency
+        test_bt_a2dp_range_codec_SBC_with_BLE_scan_low_power
+        test_bt_a2dp_range_codec_SBC_with_BLE_scan_opportunistic
+    """
 
     def __init__(self, configs):
         super().__init__(configs)
         req_params = ['attenuation_vector', 'codecs']
-        #'attenuation_vector' is a dict containing: start, stop and step of
-        #attenuation changes
+        opt_params = ['gain_mismatch', 'dual_chain']
+        #'attenuation_vector' is a dict containing: start, stop and step of attenuation changes
         #'codecs' is a list containing all codecs required in the tests
+        #'gain_mismatch' is an offset value between the BT two chains
+        #'dual_chain' set to 1 enable sweeping attenuation for BT two chains
         self.unpack_userparams(req_params)
+        self.unpack_userparams(opt_params, dual_chian=None, gain_mismatch=None)
+
+    def setup_generated_tests(self):
         for codec_config in self.codecs:
-            # Loop all BLE Scan modes
             for scan_mode in ble_scan_settings_modes.items():
-                self.generate_test_case(codec_config, scan_mode)
+                arg_set = [(codec_config, scan_mode)]
+                self.generate_tests(
+                    test_logic=self.BtA2dp_with_ble_scan_test_logic,
+                    name_func=self.create_test_name,
+                    arg_sets=arg_set)
 
     def setup_class(self):
         super().setup_class()
-        opt_params = ['gain_mismatch', 'dual_chain']
-        self.unpack_userparams(opt_params, dual_chain=None, gain_mismatch=None)
-        return setup_multiple_devices_for_bt_test(self.android_devices)
-        # Enable BQR on all android devices
+        #Enable BQR on all android devices
         btutils.enable_bqr(self.android_devices)
         if hasattr(self, 'dual_chain') and self.dual_chain == 1:
             self.atten_c0 = self.attenuators[0]
             self.atten_c1 = self.attenuators[1]
             self.atten_c0.set_atten(INIT_ATTEN)
             self.atten_c1.set_atten(INIT_ATTEN)
+        return setup_multiple_devices_for_bt_test(self.android_devices)
 
     def teardown_class(self):
         super().teardown_class()
@@ -58,31 +76,20 @@
             self.atten_c0.set_atten(INIT_ATTEN)
             self.atten_c1.set_atten(INIT_ATTEN)
 
-    def generate_test_case(self, codec_config, scan_mode):
-        """ Below are the list of test case's user can choose to run.
-        Test case list:
-        "test_bt_a2dp_range_codec_AAC_with_BLE_scan_balanced"
-        "test_bt_a2dp_range_codec_AAC_with_BLE_scan_low_latency"
-        "test_bt_a2dp_range_codec_AAC_with_BLE_scan_low_power"
-        "test_bt_a2dp_range_codec_AAC_with_BLE_scan_opportunistic"
-        "test_bt_a2dp_range_codec_SBC_with_BLE_scan_balanced"
-        "test_bt_a2dp_range_codec_SBC_with_BLE_scan_low_latency"
-        "test_bt_a2dp_range_codec_SBC_with_BLE_scan_low_power"
-        "test_bt_a2dp_range_codec_SBC_with_BLE_scan_opportunistic"
-        """
-        def test_case_fn():
-            scan_callback = self.start_ble_scan(scan_mode[1])
-            self.run_a2dp_to_max_range(codec_config)
-            self.dut.droid.bleStopBleScan(scan_callback)
-            self.log.info("BLE Scan stopped succssfully")
+    def BtA2dp_with_ble_scan_test_logic(self, codec_config, scan_mode):
+        scan_callback = self.start_ble_scan(scan_mode[1])
+        self.run_a2dp_to_max_range(codec_config)
+        self.dut.droid.bleStopBleScan(scan_callback)
+        self.log.info("BLE Scan stopped successfully")
 
+    def create_test_name(self, codec_config, scan_mode):
         if hasattr(self, 'dual_chain') and self.dual_chain == 1:
-            test_case_name = 'test_dual_bt_a2dp_range_codec_{}_gainmimatch_{}dB'.format(
-                codec_config['codec_type'], self.gain_mismatch)
+            test_case_name = 'test_dual_bt_a2dp_range_codec_{}_gainmismatch_{}dB_with_BLE_scan_{}'.format(
+                codec_config['codec_type'], self.gain_mismatch, scan_mode[0])
         else:
             test_case_name = 'test_bt_a2dp_range_codec_{}_with_BLE_scan_{}'.format(
                 codec_config['codec_type'], scan_mode[0])
-        setattr(self, test_case_name, test_case_fn)
+        return test_case_name
 
     def start_ble_scan(self, scan_mode):
         """ This function will start Ble Scan with different scan mode.
@@ -99,5 +106,6 @@
             self.dut.droid)
         self.dut.droid.bleStartBleScan(filter_list, scan_settings,
                                        scan_callback)
-        self.log.info("BLE Scanning started succssfully")
+        self.log.info("BLE Scanning started successfully")
         return scan_callback
+
diff --git a/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py b/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py
index 99218b0..6ec585d 100644
--- a/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py
+++ b/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py
@@ -29,49 +29,39 @@
 
 
 class BtInterferenceStaticTest(BtInterferenceBaseTest):
+
     def __init__(self, configs):
         super().__init__(configs)
         self.bt_attenuation_range = range(self.attenuation_vector['start'],
                                           self.attenuation_vector['stop'] + 1,
                                           self.attenuation_vector['step'])
-
         self.iperf_duration = self.audio_params['duration'] + TIME_OVERHEAD
-        for level in list(
-                self.static_wifi_interference['interference_level'].keys()):
-            for channels in self.static_wifi_interference['channels']:
-                self.generate_test_case(
-                    self.static_wifi_interference['interference_level'][level],
-                    channels)
-
         test_metrics = [
-            'wifi_chan1_rssi', 'wifi_chan6_rssi', 'wifi_chan11_rssi',
-            'bt_range'
+            'wifi_chan1_rssi', 'wifi_chan6_rssi', 'wifi_chan11_rssi', 'bt_range'
         ]
         for metric in test_metrics:
             setattr(self, '{}_metric'.format(metric),
                     BlackboxMetricLogger.for_test_case(metric_name=metric))
 
-    def generate_test_case(self, interference_level, channels):
-        """Function to generate test cases with different parameters.
+    def setup_generated_tests(self):
+        for level in list(
+                self.static_wifi_interference['interference_level'].values()):
+            for channels in self.static_wifi_interference['channels']:
+                arg_set = [(level, channels)]
+                self.generate_tests(
+                    test_logic=self.bt_range_with_static_wifi_interference,
+                    name_func=self.create_test_name,
+                    arg_sets=arg_set)
 
-        Args:
-           interference_level: wifi interference signal level
-           channels: wifi interference channel or channel combination
-        """
-        def test_case_fn():
-            self.bt_range_with_static_wifi_interference(
-                interference_level, channels)
-
+    def create_test_name(self, level, channels):
         str_channel_test = ''
         for i in channels:
-            str_channel_test = str_channel_test + str(i) + '_'
+            str_channel_test = str_channel_test + str(i) + "_"
         test_case_name = ('test_bt_range_with_static_interference_level_{}_'
-                          'channel_{}'.format(interference_level,
-                                              str_channel_test))
-        setattr(self, test_case_name, test_case_fn)
+                          'channel_{}'.format(level, str_channel_test))
+        return test_case_name
 
-    def bt_range_with_static_wifi_interference(self, interference_level,
-                                               channels):
+    def bt_range_with_static_wifi_interference(self, level, channels):
         """Test function to measure bt range under interference.
 
         Args:
@@ -79,8 +69,7 @@
             channels: wifi interference channels
         """
         #setup wifi interference by setting the correct attenuator
-        inject_static_wifi_interference(self.wifi_int_pairs,
-                                        interference_level, channels)
+        inject_static_wifi_interference(self.wifi_int_pairs, level, channels)
         # Read interference RSSI
         self.get_interference_rssi()
         self.wifi_chan1_rssi_metric.metric_value = self.interference_rssi[0][
@@ -113,8 +102,7 @@
                     self.iperf_duration, obj.iperf_server.port)
                 tag = 'chan_{}'.format(obj.channel)
                 proc_iperf = Process(target=obj.iperf_client.start,
-                                     args=(obj.server_address, iperf_args,
-                                           tag))
+                                     args=(obj.server_address, iperf_args, tag))
                 procs_iperf.append(proc_iperf)
 
             #play a2dp streaming and run thdn analysis
@@ -140,8 +128,8 @@
             self.log.info('THDN results are {} at {} dB attenuation'.format(
                 thdns, atten))
             self.log.info('DUT rssi {} dBm, master tx power level {}, '
-                          'RemoteDevice rssi {} dBm'.format(rssi_primary, pwl_primary,
-                                                     rssi_secondary))
+                          'RemoteDevice rssi {} dBm'.format(
+                              rssi_primary, pwl_primary, rssi_secondary))
             for thdn in thdns:
                 if thdn >= self.audio_params['thdn_threshold']:
                     self.log.info('Under the WiFi interference condition: '
@@ -154,3 +142,4 @@
                     raise TestPass(
                         'Max range for this test is {}, with BT master RSSI at'
                         ' {} dBm'.format(atten, rssi_primary))
+
diff --git a/acts_tests/tests/google/bt/performance/BtRfcommThroughputRangeTest.py b/acts_tests/tests/google/bt/performance/BtRfcommThroughputRangeTest.py
new file mode 100644
index 0000000..342f2f0
--- /dev/null
+++ b/acts_tests/tests/google/bt/performance/BtRfcommThroughputRangeTest.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import logging
+import pandas as pd
+import time
+import acts_contrib.test_utils.bt.bt_test_utils as btutils
+from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
+from acts_contrib.test_utils.bt.ble_performance_test_utils import plot_graph
+from acts_contrib.test_utils.power.PowerBTBaseTest import ramp_attenuation
+from acts_contrib.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
+from acts_contrib.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection
+from acts.signals import TestPass
+from acts import utils
+from acts_contrib.test_utils.bt.bt_test_utils import write_read_verify_data
+
+INIT_ATTEN = 0
+WRITE_ITERATIONS = 500
+
+
+class BtRfcommThroughputRangeTest(BluetoothBaseTest):
+    def __init__(self, configs):
+        super().__init__(configs)
+        req_params = ['attenuation_vector', 'system_path_loss']
+        #'attenuation_vector' is a dict containing: start, stop and step of
+        #attenuation changes
+        self.unpack_userparams(req_params)
+
+    def setup_class(self):
+        super().setup_class()
+        self.dut = self.android_devices[0]
+        self.remote_device = self.android_devices[1]
+        btutils.enable_bqr(self.android_devices)
+        if hasattr(self, 'attenuators'):
+            self.attenuator = self.attenuators[0]
+            self.attenuator.set_atten(INIT_ATTEN)
+        self.attenuation_range = range(self.attenuation_vector['start'],
+                                       self.attenuation_vector['stop'] + 1,
+                                       self.attenuation_vector['step'])
+        self.log_path = os.path.join(logging.log_path, 'results')
+        os.makedirs(self.log_path, exist_ok=True)
+        return setup_multiple_devices_for_bt_test(self.android_devices)
+
+    def teardown_test(self):
+        self.dut.droid.bluetoothSocketConnStop()
+        self.remote_device.droid.bluetoothSocketConnStop()
+        if hasattr(self, 'attenuator'):
+            self.attenuator.set_atten(INIT_ATTEN)
+
+    def test_rfcomm_throughput_range(self):
+        data_points = []
+        message = "x" * 990
+        self.file_output = os.path.join(
+            self.log_path, '{}.csv'.format(self.current_test_name))
+        if not orchestrate_rfcomm_connection(self.dut, self.remote_device):
+            return False
+        self.log.info("RFCOMM Connection established")
+        for atten in self.attenuation_range:
+            ramp_attenuation(self.attenuator, atten)
+            self.log.info('Set attenuation to %d dB', atten)
+            process_data_dict = btutils.get_bt_metric(self.dut)
+            rssi_primary = process_data_dict.get('rssi')
+            pwlv_primary = process_data_dict.get('pwlv')
+            rssi_primary = rssi_primary.get(self.dut.serial)
+            pwlv_primary = pwlv_primary.get(self.dut.serial)
+            self.log.info("DUT RSSI:{} and PwLv:{} with attenuation:{}".format(
+                rssi_primary, pwlv_primary, atten))
+            if type(rssi_primary) != str:
+                data_rate = self.write_read_verify_rfcommdata(
+                    self.dut, self.remote_device, message)
+                data_point = {
+                    'attenuation_db': atten,
+                    'Dut_RSSI': rssi_primary,
+                    'DUT_PwLv': pwlv_primary,
+                    'Pathloss': atten + self.system_path_loss,
+                    'RfcommThroughput': data_rate
+                }
+                data_points.append(data_point)
+                df = pd.DataFrame(data_points)
+                # bokeh data for generating BokehFigure
+                bokeh_data = {
+                    'x_label': 'Pathloss (dBm)',
+                    'primary_y_label': 'RSSI (dBm)',
+                    'log_path': self.log_path,
+                    'current_test_name': self.current_test_name
+                }
+                # plot_data for adding line to existing BokehFigure
+                plot_data = {
+                    'line_one': {
+                        'x_column': 'Pathloss',
+                        'y_column': 'Dut_RSSI',
+                        'legend': 'DUT RSSI (dBm)',
+                        'marker': 'circle_x',
+                        'y_axis': 'default'
+                    },
+                    'line_two': {
+                        'x_column': 'Pathloss',
+                        'y_column': 'RfcommThroughput',
+                        'legend': 'RFCOMM Throughput (bits/sec)',
+                        'marker': 'hex',
+                        'y_axis': 'secondary'
+                    }
+                }
+            else:
+                df.to_csv(self.file_output, index=False)
+                plot_graph(df,
+                           plot_data,
+                           bokeh_data,
+                           secondary_y_label='RFCOMM Throughput (bits/sec)')
+                raise TestPass("Reached RFCOMM Max Range,RFCOMM disconnected.")
+        # Save Data points to csv
+        df.to_csv(self.file_output, index=False)
+        # Plot graph
+        plot_graph(df,
+                   plot_data,
+                   bokeh_data,
+                   secondary_y_label='RFCOMM Throughput (bits/sec)')
+        self.dut.droid.bluetoothRfcommStop()
+        self.remote_device.droid.bluetoothRfcommStop()
+        return True
+
+    def write_read_verify_rfcommdata(self, dut, remote_device, msg):
+        """Verify that the client wrote data to the remote Android device correctly.
+
+        Args:
+            dut: the Android device to perform the write.
+            remote_device: the Android device to read the data written.
+            msg: the message to write.
+        Returns:
+            True if the data written matches the data read, false if not.
+        """
+        start_write_time = time.perf_counter()
+        for n in range(WRITE_ITERATIONS):
+            try:
+                dut.droid.bluetoothSocketConnWrite(msg)
+            except Exception as err:
+                dut.log.error("Failed to write data: {}".format(err))
+                return False
+            try:
+                read_msg = remote_device.droid.bluetoothSocketConnRead()
+            except Exception as err:
+                remote_device.log.error("Failed to read data: {}".format(err))
+                return False
+            if msg != read_msg:
+                self.log.error("Mismatch! Read: {}, Expected: {}".format(
+                    read_msg, msg))
+                return False
+        end_read_time = time.perf_counter()
+        total_num_bytes = 990 * WRITE_ITERATIONS
+        test_time = (end_read_time - start_write_time)
+        if (test_time == 0):
+            dut.log.error("Buffer transmits cannot take zero time")
+            return 0
+        data_rate = (1.000 * total_num_bytes) / test_time
+        self.log.info(
+            "Calculated using total write and read times: total_num_bytes={}, "
+            "test_time={}, data rate={:08.0f} bytes/sec, {:08.0f} bits/sec".
+            format(total_num_bytes, test_time, data_rate, (data_rate * 8)))
+        data_rate = data_rate * 8
+        return data_rate
diff --git a/acts_tests/tests/google/bt/performance/InjectWifiInterferenceTest.py b/acts_tests/tests/google/bt/performance/InjectWifiInterferenceTest.py
index 22c2b2f..8949430 100644
--- a/acts_tests/tests/google/bt/performance/InjectWifiInterferenceTest.py
+++ b/acts_tests/tests/google/bt/performance/InjectWifiInterferenceTest.py
@@ -13,11 +13,8 @@
 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 # License for the specific language governing permissions and limitations under
 # the License.
-import json
 import random
-import sys
 import logging
-import re
 from acts.base_test import BaseTestClass
 from acts_contrib.test_utils.bt.BtInterferenceBaseTest import inject_static_wifi_interference
 from acts_contrib.test_utils.bt.BtInterferenceBaseTest import unpack_custom_file
diff --git a/acts_tests/tests/google/bt/performance/StartIperfTrafficTest.py b/acts_tests/tests/google/bt/performance/StartIperfTrafficTest.py
index edc373f..7ecf3bc 100644
--- a/acts_tests/tests/google/bt/performance/StartIperfTrafficTest.py
+++ b/acts_tests/tests/google/bt/performance/StartIperfTrafficTest.py
@@ -13,12 +13,11 @@
 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 # License for the specific language governing permissions and limitations under
 # the License.
-import sys
 import time
 import acts.controllers.iperf_client as ipc
 from acts_contrib.test_utils.bt.BtInterferenceBaseTest import BtInterferenceBaseTest
 from acts_contrib.test_utils.power.PowerBaseTest import ObjNew
-from multiprocessing import Process, Queue
+from multiprocessing import Process
 from acts_contrib.test_utils.bt.BtInterferenceBaseTest import setup_ap_connection
 from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils
 from acts.signals import TestPass
@@ -27,9 +26,10 @@
 class StartIperfTrafficTest(BtInterferenceBaseTest):
     """
     """
+
     def __init__(self, configs):
         super().__init__(configs)
-        req_params =["IperfDuration"]
+        req_params = ["IperfDuration"]
         self.unpack_userparams(req_params)
 
     def setup_class(self):
diff --git a/acts_tests/tests/google/bt/power/SetupBTPairingTest.py b/acts_tests/tests/google/bt/power/SetupBTPairingTest.py
index 896d009..bfcef3b 100644
--- a/acts_tests/tests/google/bt/power/SetupBTPairingTest.py
+++ b/acts_tests/tests/google/bt/power/SetupBTPairingTest.py
@@ -13,7 +13,6 @@
 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 # License for the specific language governing permissions and limitations under
 # the License.
-
 """
 This test script leverages the relay_lib to pair different BT devices. This
 script will be invoked from Tradefed test. The test will first setup pairing
@@ -23,13 +22,12 @@
 
 import logging
 import socket
-import sys
 import time
 
 from acts import base_test
 
-class SetupBTPairingTest(base_test.BaseTestClass):
 
+class SetupBTPairingTest(base_test.BaseTestClass):
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
 
@@ -40,7 +38,8 @@
         return self.relay_devices[0]
 
     def setup_test(self):
-        self.bt_device = self.select_device_by_mac_address(self.user_params["mac_address"])
+        self.bt_device = self.select_device_by_mac_address(
+            self.user_params["mac_address"])
 
     def wait_for_test_completion(self):
         port = int(self.user_params["socket_port"])
@@ -68,7 +67,6 @@
                 sock.shutdown(socket.SHUT_RDWR)
                 sock.close()
 
-
     def enable_pairing_mode(self):
         self.bt_device.setup()
         self.bt_device.power_on()
@@ -77,12 +75,10 @@
         self.bt_device.enter_pairing_mode()
 
     def test_bt_pairing(self):
-        req_params = [
-            "RelayDevice", "socket_port", "socket_timeout_secs"
-        ]
+        req_params = ["RelayDevice", "socket_port", "socket_timeout_secs"]
         opt_params = []
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_params)
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_params)
         # Setup BT pairing mode
         self.enable_pairing_mode()
         # BT pairing mode is turned on
diff --git a/acts_tests/tests/google/bt/pts/BtCmdLineTest.py b/acts_tests/tests/google/bt/pts/BtCmdLineTest.py
index 16e1aa0..98a5302 100644
--- a/acts_tests/tests/google/bt/pts/BtCmdLineTest.py
+++ b/acts_tests/tests/google/bt/pts/BtCmdLineTest.py
@@ -23,10 +23,8 @@
 """
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from cmd_input import CmdInput
-from queue import Empty
 
 import os
-import uuid
 
 from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
 
diff --git a/acts_tests/tests/google/bt/system_tests/RfcommLongevityTest.py b/acts_tests/tests/google/bt/system_tests/RfcommLongevityTest.py
index 356ac8d..b571b8d 100644
--- a/acts_tests/tests/google/bt/system_tests/RfcommLongevityTest.py
+++ b/acts_tests/tests/google/bt/system_tests/RfcommLongevityTest.py
@@ -18,10 +18,8 @@
 This test was designed to be run in a shield box.
 """
 
-import time
 from random import randint
 
-from queue import Empty
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
@@ -78,8 +76,8 @@
                                                  self.server_ad):
                 return False
             for n in range(self.write_iterations):
-                self.log.info("iteration {} data".format(((n + 1) + (
-                    i * self.write_iterations))))
+                self.log.info("iteration {} data".format(
+                    ((n + 1) + (i * self.write_iterations))))
                 if not write_read_verify_data(self.client_ad, self.server_ad,
                                               self.generic_message, False):
                     return False
@@ -123,8 +121,8 @@
                                                  self.server_ad):
                 return False
             for n in range(self.write_iterations):
-                self.log.info("iteration {} data".format(((n + 1) + (
-                    i * self.write_iterations))))
+                self.log.info("iteration {} data".format(
+                    ((n + 1) + (i * self.write_iterations))))
                 if not write_read_verify_data(self.client_ad, self.server_ad,
                                               message, False):
                     return False
@@ -168,8 +166,8 @@
                                                  self.server_ad):
                 return False
             for n in range(self.write_iterations):
-                self.log.info("iteration {} data".format(((n + 1) + (
-                    i * self.write_iterations))))
+                self.log.info("iteration {} data".format(
+                    ((n + 1) + (i * self.write_iterations))))
                 if not write_read_verify_data(self.client_ad, self.server_ad,
                                               binary_message, True):
                     return False
@@ -213,8 +211,8 @@
                                                  self.server_ad):
                 return False
             for n in range(self.write_iterations):
-                self.log.info("iteration {} data".format(((n + 1) + (
-                    i * self.write_iterations))))
+                self.log.info("iteration {} data".format(
+                    ((n + 1) + (i * self.write_iterations))))
                 if not write_read_verify_data(self.client_ad, self.server_ad,
                                               message, False):
                     return False
@@ -262,8 +260,8 @@
                     return False
                 random_interup_iteration = randint(0, self.write_iterations)
                 for n in range(self.write_iterations):
-                    self.log.info("iteration {} data".format(((n + 1) + (
-                        i * self.write_iterations))))
+                    self.log.info("iteration {} data".format(
+                        ((n + 1) + (i * self.write_iterations))))
                     if not write_read_verify_data(self.client_ad,
                                                   self.server_ad,
                                                   self.generic_message, False):
@@ -330,8 +328,8 @@
                                                      self.server_ad):
                     return False
                 for n in range(self.write_iterations):
-                    self.log.info("iteration {} data".format(((n + 1) + (
-                        i * self.write_iterations))))
+                    self.log.info("iteration {} data".format(
+                        ((n + 1) + (i * self.write_iterations))))
                     if not write_read_verify_data(
                             self.client_ad, self.server_ad, message, False):
                         return False
diff --git a/acts_tests/tests/google/bt/system_tests/RfcommStressTest.py b/acts_tests/tests/google/bt/system_tests/RfcommStressTest.py
index ea9d999..fbd989b 100644
--- a/acts_tests/tests/google/bt/system_tests/RfcommStressTest.py
+++ b/acts_tests/tests/google/bt/system_tests/RfcommStressTest.py
@@ -18,10 +18,6 @@
 This test was designed to be run in a shield box.
 """
 
-import threading
-import time
-
-from queue import Empty
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
diff --git a/acts_tests/tests/google/bt/test_tools/EnergyTest.py b/acts_tests/tests/google/bt/test_tools/EnergyTest.py
index 7797f8a..8db0cc0 100644
--- a/acts_tests/tests/google/bt/test_tools/EnergyTest.py
+++ b/acts_tests/tests/google/bt/test_tools/EnergyTest.py
@@ -17,7 +17,6 @@
 Continuously poll for energy info for a single Android Device
 """
 
-from queue import Empty
 from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 
 
@@ -30,8 +29,8 @@
     def test_continuous_energy_report(self):
         while (True):
             try:
-                self.log.info(self.android_devices[
-                    0].droid.bluetoothGetControllerActivityEnergyInfo(1))
+                self.log.info(self.android_devices[0].droid.
+                              bluetoothGetControllerActivityEnergyInfo(1))
             except Exception:
                 self.log.error("Failed to log energy info... continuing.")
         return True
diff --git a/acts_tests/tests/google/cellular/performance/Cellular5GFR2SensitivityTest.py b/acts_tests/tests/google/cellular/performance/Cellular5GFR2SensitivityTest.py
new file mode 100644
index 0000000..f3e49b8
--- /dev/null
+++ b/acts_tests/tests/google/cellular/performance/Cellular5GFR2SensitivityTest.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import itertools
+import json
+import numpy
+import os
+from functools import partial
+from acts import asserts
+from acts import context
+from acts import base_test
+from acts import utils
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.controllers.utils_lib import ssh
+from acts_contrib.test_utils.cellular.keysight_5g_testapp import Keysight5GTestApp
+from acts_contrib.test_utils.cellular.performance import cellular_performance_test_utils as cputils
+from acts_contrib.test_utils.wifi.wifi_performance_test_utils.bokeh_figure import BokehFigure
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from Cellular5GFR2ThroughputTest import Cellular5GFR2ThroughputTest
+
+
+class Cellular5GFR2SensitivityTest(Cellular5GFR2ThroughputTest):
+    """Class to test cellular throughput
+
+    This class implements cellular throughput tests on a lab/callbox setup.
+    The class setups up the callbox in the desired configurations, configures
+    and connects the phone, and runs traffic/iperf throughput.
+    """
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+
+    def setup_class(self):
+        """Initializes common test hardware and parameters.
+
+        This function initializes hardwares and compiles parameters that are
+        common to all tests in this class.
+        """
+        self.dut = self.android_devices[-1]
+        self.testclass_params = self.user_params['sensitivity_test_params']
+        self.keysight_test_app = Keysight5GTestApp(
+            self.user_params['Keysight5GTestApp'])
+        self.testclass_results = collections.OrderedDict()
+        self.iperf_server = self.iperf_servers[0]
+        self.iperf_client = self.iperf_clients[0]
+        self.remote_server = ssh.connection.SshConnection(
+            ssh.settings.from_config(
+                self.user_params['RemoteServer']['ssh_config']))
+        if self.testclass_params.get('reload_scpi', 1):
+            self.keysight_test_app.import_scpi_file(
+                self.testclass_params['scpi_file'])
+        # Configure test retries
+        self.user_params['retry_tests'] = [self.__class__.__name__]
+
+        # Turn Airplane mode on
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+
+    def process_testcase_results(self):
+        if self.current_test_name not in self.testclass_results:
+            return
+        testcase_results = self.testclass_results[self.current_test_name]
+        cell_power_list = [
+            result['cell_power'] for result in testcase_results['results']
+        ]
+        dl_bler_list = [
+            result['bler_result']['total']['DL']['nack_ratio']
+            for result in testcase_results['results']
+        ]
+        bler_above_threshold = [
+            x > self.testclass_params['bler_threshold'] for x in dl_bler_list
+        ]
+        for idx in range(len(bler_above_threshold)):
+            if all(bler_above_threshold[idx:]):
+                sensitivity_index = max(idx, 1) - 1
+                cell_power_at_sensitivity = cell_power_list[sensitivity_index]
+                break
+        else:
+            sensitivity_index = -1
+            cell_power_at_sensitivity = float('nan')
+        if min(dl_bler_list) < 0.05:
+            testcase_results['sensitivity'] = cell_power_at_sensitivity
+        else:
+            testcase_results['sensitivity'] = float('nan')
+
+        testcase_results['cell_power_list'] = cell_power_list
+        testcase_results['dl_bler_list'] = dl_bler_list
+
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            '{}.json'.format(self.current_test_name))
+        with open(results_file_path, 'w') as results_file:
+            json.dump(wputils.serialize_dict(testcase_results),
+                      results_file,
+                      indent=4)
+
+        result_string = ('DL {}CC MCS {} Sensitivity = {}dBm.'.format(
+            testcase_results['testcase_params']['num_dl_cells'],
+            testcase_results['testcase_params']['dl_mcs'],
+            testcase_results['sensitivity']))
+        if min(dl_bler_list) < 0.05:
+            self.log.info('Test Passed. {}'.format(result_string))
+        else:
+            self.log.info('Result unreliable. {}'.format(result_string))
+
+    def process_testclass_results(self):
+        Cellular5GFR2ThroughputTest.process_testclass_results(self)
+
+        plots = collections.OrderedDict()
+        id_fields = ['band', 'num_dl_cells']
+        for testcase, testcase_data in self.testclass_results.items():
+            testcase_params = testcase_data['testcase_params']
+            plot_id = cputils.extract_test_id(testcase_params, id_fields)
+            plot_id = tuple(plot_id.items())
+            if plot_id not in plots:
+                plots[plot_id] = BokehFigure(title='{} {}CC'.format(
+                    testcase_params['band'], testcase_params['num_dl_cells']),
+                                             x_label='Cell Power (dBm)',
+                                             primary_y_label='BLER (%)')
+            plots[plot_id].add_line(
+                testcase_data['cell_power_list'],
+                testcase_data['dl_bler_list'],
+                'Channel {}, MCS {}'.format(testcase_params['channel'],
+                                            testcase_params['dl_mcs']))
+        figure_list = []
+        for plot_id, plot in plots.items():
+            plot.generate_figure()
+            figure_list.append(plot)
+        output_file_path = os.path.join(self.log_path, 'results.html')
+        BokehFigure.save_figures(figure_list, output_file_path)
+
+    def generate_test_cases(self, bands, channels, mcs_pair_list,
+                            num_dl_cells_list, num_ul_cells_list, **kwargs):
+        """Function that auto-generates test cases for a test class."""
+        test_cases = []
+
+        for band, channel, num_ul_cells, num_dl_cells, mcs_pair in itertools.product(
+                bands, channels, num_ul_cells_list, num_dl_cells_list,
+                mcs_pair_list):
+            if num_ul_cells > num_dl_cells:
+                continue
+            test_name = 'test_nr_sensitivity_{}_{}_DL_{}CC_mcs{}'.format(
+                band, channel, num_dl_cells, mcs_pair[0])
+            test_params = collections.OrderedDict(
+                band=band,
+                channel=channel,
+                dl_mcs=mcs_pair[0],
+                ul_mcs=mcs_pair[1],
+                num_dl_cells=num_dl_cells,
+                num_ul_cells=num_ul_cells,
+                dl_cell_list=list(range(1, num_dl_cells + 1)),
+                ul_cell_list=list(range(1, num_ul_cells + 1)),
+                **kwargs)
+            setattr(self, test_name,
+                    partial(self._test_nr_throughput_bler, test_params))
+            test_cases.append(test_name)
+        return test_cases
+
+
+class Cellular5GFR2_AllBands_SensitivityTest(Cellular5GFR2SensitivityTest):
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                              ['low', 'mid', 'high'],
+                                              [(16, 4), (27, 4)],
+                                              list(range(1, 9)), [1],
+                                              schedule_scenario="FULL_TPUT",
+                                              traffic_direction='DL',
+                                              transform_precoding=0)
+
+
+class Cellular5GFR2_FrequencySweep_SensitivityTest(Cellular5GFR2SensitivityTest
+                                                   ):
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        frequency_sweep_params = self.user_params['sensitivity_test_params'][
+            'frequency_sweep']
+        self.tests = self.generate_test_cases(frequency_sweep_params,
+                                              [(16, 4), (27, 4)],
+                                              schedule_scenario="FULL_TPUT",
+                                              traffic_direction='DL',
+                                              transform_precoding=0)
+
+    def generate_test_cases(self, dl_frequency_sweep_params, mcs_pair_list,
+                            **kwargs):
+        """Function that auto-generates test cases for a test class."""
+        test_cases = ['test_load_scpi']
+
+        for band, band_config in dl_frequency_sweep_params.items():
+            for num_dl_cells_str, sweep_config in band_config.items():
+                num_dl_cells = int(num_dl_cells_str[0])
+                num_ul_cells = 1
+                freq_vector = numpy.arange(sweep_config[0], sweep_config[1],
+                                           sweep_config[2])
+                for freq in freq_vector:
+                    for mcs_pair in mcs_pair_list:
+                        test_name = 'test_nr_sensitivity_{}_{}_DL_{}CC_mcs{}'.format(
+                            band, freq, num_dl_cells, mcs_pair[0])
+                        test_params = collections.OrderedDict(
+                            band=band,
+                            channel=freq,
+                            dl_mcs=mcs_pair[0],
+                            ul_mcs=mcs_pair[1],
+                            num_dl_cells=num_dl_cells,
+                            num_ul_cells=num_ul_cells,
+                            dl_cell_list=list(range(1, num_dl_cells + 1)),
+                            ul_cell_list=list(range(1, num_ul_cells + 1)),
+                            **kwargs)
+                        setattr(
+                            self, test_name,
+                            partial(self._test_nr_throughput_bler,
+                                    test_params))
+                        test_cases.append(test_name)
+        return test_cases
diff --git a/acts_tests/tests/google/cellular/performance/Cellular5GFR2ThroughputTest.py b/acts_tests/tests/google/cellular/performance/Cellular5GFR2ThroughputTest.py
new file mode 100644
index 0000000..9e848a3
--- /dev/null
+++ b/acts_tests/tests/google/cellular/performance/Cellular5GFR2ThroughputTest.py
@@ -0,0 +1,664 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import csv
+import itertools
+import json
+import numpy
+import os
+import time
+from acts import asserts
+from acts import context
+from acts import base_test
+from acts import utils
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.controllers.utils_lib import ssh
+from acts.controllers import iperf_server as ipf
+from acts_contrib.test_utils.cellular.keysight_5g_testapp import Keysight5GTestApp
+from acts_contrib.test_utils.cellular.performance import cellular_performance_test_utils as cputils
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+
+from functools import partial
+
+LONG_SLEEP = 10
+MEDIUM_SLEEP = 2
+IPERF_TIMEOUT = 10
+SHORT_SLEEP = 1
+SUBFRAME_LENGTH = 0.001
+STOP_COUNTER_LIMIT = 3
+
+
+class Cellular5GFR2ThroughputTest(base_test.BaseTestClass):
+    """Class to test cellular throughput
+
+    This class implements cellular throughput tests on a lab/callbox setup.
+    The class setups up the callbox in the desired configurations, configures
+    and connects the phone, and runs traffic/iperf throughput.
+    """
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+
+    def setup_class(self):
+        """Initializes common test hardware and parameters.
+
+        This function initializes hardwares and compiles parameters that are
+        common to all tests in this class.
+        """
+        self.dut = self.android_devices[-1]
+        self.testclass_params = self.user_params['throughput_test_params']
+        self.keysight_test_app = Keysight5GTestApp(
+            self.user_params['Keysight5GTestApp'])
+        self.testclass_results = collections.OrderedDict()
+        self.iperf_server = self.iperf_servers[0]
+        self.iperf_client = self.iperf_clients[0]
+        self.remote_server = ssh.connection.SshConnection(
+            ssh.settings.from_config(
+                self.user_params['RemoteServer']['ssh_config']))
+        if self.testclass_params.get('reload_scpi', 1):
+            self.keysight_test_app.import_scpi_file(
+                self.testclass_params['scpi_file'])
+        # Configure test retries
+        self.user_params['retry_tests'] = [self.__class__.__name__]
+
+        # Turn Airplane mode on
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+
+    def teardown_class(self):
+        self.log.info('Turning airplane mode on')
+        try:
+            asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                                'Can not turn on airplane mode.')
+        except:
+            self.log.warning('Cannot perform teardown operations on DUT.')
+        try:
+            self.keysight_test_app.set_cell_state('LTE', 1, 0)
+            self.keysight_test_app.destroy()
+        except:
+            self.log.warning('Cannot perform teardown operations on tester.')
+        self.process_testclass_results()
+
+    def setup_test(self):
+        if self.testclass_params['enable_pixel_logs']:
+            cputils.start_pixel_logger(self.dut)
+
+    def on_retry(self):
+        """Function to control test logic on retried tests.
+
+        This function is automatically executed on tests that are being
+        retried. In this case the function resets wifi, toggles it off and on
+        and sets a retry_flag to enable further tweaking the test logic on
+        second attempts.
+        """
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+        if self.keysight_test_app.get_cell_state('LTE', 'CELL1'):
+            self.log.info('Turning LTE off.')
+            self.keysight_test_app.set_cell_state('LTE', 'CELL1', 0)
+
+    def teardown_test(self):
+        self.log.info('Turing airplane mode on')
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+        log_path = os.path.join(
+            context.get_current_context().get_full_output_path(), 'pixel_logs')
+        os.makedirs(self.log_path, exist_ok=True)
+        if self.testclass_params['enable_pixel_logs']:
+            cputils.stop_pixel_logger(self.dut, log_path)
+        self.process_testcase_results()
+        self.pass_fail_check()
+
+    def process_testcase_results(self):
+        if self.current_test_name not in self.testclass_results:
+            return
+        testcase_data = self.testclass_results[self.current_test_name]
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            '{}.json'.format(self.current_test_name))
+        with open(results_file_path, 'w') as results_file:
+            json.dump(wputils.serialize_dict(testcase_data),
+                      results_file,
+                      indent=4)
+        testcase_result = testcase_data['results'][0]
+        metric_map = {
+            'min_dl_tput':
+            testcase_result['tput_result']['total']['DL']['min_tput'],
+            'max_dl_tput':
+            testcase_result['tput_result']['total']['DL']['max_tput'],
+            'avg_dl_tput':
+            testcase_result['tput_result']['total']['DL']['average_tput'],
+            'theoretical_dl_tput':
+            testcase_result['tput_result']['total']['DL']['theoretical_tput'],
+            'dl_bler':
+            testcase_result['bler_result']['total']['DL']['nack_ratio'] * 100,
+            'min_dl_tput':
+            testcase_result['tput_result']['total']['UL']['min_tput'],
+            'max_dl_tput':
+            testcase_result['tput_result']['total']['UL']['max_tput'],
+            'avg_dl_tput':
+            testcase_result['tput_result']['total']['UL']['average_tput'],
+            'theoretical_dl_tput':
+            testcase_result['tput_result']['total']['UL']['theoretical_tput'],
+            'ul_bler':
+            testcase_result['bler_result']['total']['UL']['nack_ratio'] * 100,
+            'tcp_udp_tput':
+            testcase_result.get('iperf_throughput', float('nan'))
+        }
+        if self.publish_testcase_metrics:
+            for metric_name, metric_value in metric_map.items():
+                self.testcase_metric_logger.add_metric(metric_name,
+                                                       metric_value)
+
+    def pass_fail_check(self):
+        pass
+
+    def process_testclass_results(self):
+        """Saves CSV with all test results to enable comparison."""
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            'results.csv')
+        with open(results_file_path, 'w', newline='') as csvfile:
+            field_names = [
+                'Band', 'Channel', 'DL Carriers', 'UL Carriers', 'DL MCS',
+                'DL MIMO', 'UL MCS', 'UL MIMO', 'Cell Power',
+                'DL Min. Throughput', 'DL Max. Throughput',
+                'DL Avg. Throughput', 'DL Theoretical Throughput',
+                'UL Min. Throughput', 'UL Max. Throughput',
+                'UL Avg. Throughput', 'UL Theoretical Throughput',
+                'DL BLER (%)', 'UL BLER (%)', 'TCP/UDP Throughput'
+            ]
+            writer = csv.DictWriter(csvfile, fieldnames=field_names)
+            writer.writeheader()
+
+            for testcase_name, testcase_results in self.testclass_results.items(
+            ):
+                for result in testcase_results['results']:
+                    writer.writerow({
+                        'Band':
+                        testcase_results['testcase_params']['band'],
+                        'Channel':
+                        testcase_results['testcase_params']['channel'],
+                        'DL Carriers':
+                        testcase_results['testcase_params']['num_dl_cells'],
+                        'UL Carriers':
+                        testcase_results['testcase_params']['num_ul_cells'],
+                        'DL MCS':
+                        testcase_results['testcase_params']['dl_mcs'],
+                        'DL MIMO':
+                        testcase_results['testcase_params']['dl_mimo_config'],
+                        'UL MCS':
+                        testcase_results['testcase_params']['ul_mcs'],
+                        'UL MIMO':
+                        testcase_results['testcase_params']['ul_mimo_config'],
+                        'Cell Power':
+                        result['cell_power'],
+                        'DL Min. Throughput':
+                        result['tput_result']['total']['DL']['min_tput'],
+                        'DL Max. Throughput':
+                        result['tput_result']['total']['DL']['max_tput'],
+                        'DL Avg. Throughput':
+                        result['tput_result']['total']['DL']['average_tput'],
+                        'DL Theoretical Throughput':
+                        result['tput_result']['total']['DL']
+                        ['theoretical_tput'],
+                        'UL Min. Throughput':
+                        result['tput_result']['total']['UL']['min_tput'],
+                        'UL Max. Throughput':
+                        result['tput_result']['total']['UL']['max_tput'],
+                        'UL Avg. Throughput':
+                        result['tput_result']['total']['UL']['average_tput'],
+                        'UL Theoretical Throughput':
+                        result['tput_result']['total']['UL']
+                        ['theoretical_tput'],
+                        'DL BLER (%)':
+                        result['bler_result']['total']['DL']['nack_ratio'] *
+                        100,
+                        'UL BLER (%)':
+                        result['bler_result']['total']['UL']['nack_ratio'] *
+                        100,
+                        'TCP/UDP Throughput':
+                        result.get('iperf_throughput', 0)
+                    })
+
+    def setup_tester(self, testcase_params):
+        if not self.keysight_test_app.get_cell_state('LTE', 'CELL1'):
+            self.log.info('Turning LTE on.')
+            self.keysight_test_app.set_cell_state('LTE', 'CELL1', 1)
+        self.log.info('Turning off airplane mode')
+        asserts.assert_true(utils.force_airplane_mode(self.dut, False),
+                            'Can not turn on airplane mode.')
+        for cell in testcase_params['dl_cell_list']:
+            self.keysight_test_app.set_cell_band('NR5G', cell,
+                                                 testcase_params['band'])
+            self.keysight_test_app.set_cell_mimo_config(
+                'NR5G', cell, 'DL', testcase_params['dl_mimo_config'])
+            self.keysight_test_app.set_cell_dl_power(
+                'NR5G', cell, testcase_params['cell_power_list'][0], 1)
+        for cell in testcase_params['ul_cell_list']:
+            self.keysight_test_app.set_cell_mimo_config(
+                'NR5G', cell, 'UL', testcase_params['ul_mimo_config'])
+        self.keysight_test_app.configure_contiguous_nr_channels(
+            testcase_params['dl_cell_list'][0], testcase_params['band'],
+            testcase_params['channel'])
+        # Consider configuring schedule quick config
+        self.keysight_test_app.set_nr_cell_schedule_scenario(
+            testcase_params['dl_cell_list'][0],
+            testcase_params['schedule_scenario'])
+        self.keysight_test_app.set_nr_ul_dft_precoding(
+            testcase_params['dl_cell_list'][0],
+            testcase_params['transform_precoding'])
+        self.keysight_test_app.set_nr_cell_mcs(
+            testcase_params['dl_cell_list'][0], testcase_params['dl_mcs'],
+            testcase_params['ul_mcs'])
+        self.keysight_test_app.set_dl_carriers(testcase_params['dl_cell_list'])
+        self.keysight_test_app.set_ul_carriers(testcase_params['ul_cell_list'])
+        self.log.info('Waiting for LTE and applying aggregation')
+        if not self.keysight_test_app.wait_for_cell_status(
+                'LTE', 'CELL1', 'CONN', 60):
+            asserts.fail('DUT did not connect to LTE.')
+        self.keysight_test_app.apply_carrier_agg()
+        self.log.info('Waiting for 5G connection')
+        connected = self.keysight_test_app.wait_for_cell_status(
+            'NR5G', testcase_params['dl_cell_list'][-1], ['ACT', 'CONN'], 60)
+        if not connected:
+            asserts.fail('DUT did not connect to NR.')
+        time.sleep(SHORT_SLEEP)
+
+    def run_iperf_traffic(self, testcase_params):
+        self.iperf_server.start(tag=0)
+        dut_ip = self.dut.droid.connectivityGetIPv4Addresses('rmnet0')[0]
+        if 'iperf_server_address' in self.testclass_params:
+            iperf_server_address = self.testclass_params[
+                'iperf_server_address']
+        elif isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+            iperf_server_address = dut_ip
+        else:
+            iperf_server_address = wputils.get_server_address(
+                self.remote_server, dut_ip, '255.255.255.0')
+        client_output_path = self.iperf_client.start(
+            iperf_server_address, testcase_params['iperf_args'], 0,
+            self.testclass_params['traffic_duration'] + IPERF_TIMEOUT)
+        server_output_path = self.iperf_server.stop()
+        # Parse and log result
+        if testcase_params['use_client_output']:
+            iperf_file = client_output_path
+        else:
+            iperf_file = server_output_path
+        try:
+            iperf_result = ipf.IPerfResult(iperf_file)
+            current_throughput = numpy.mean(iperf_result.instantaneous_rates[
+                self.testclass_params['iperf_ignored_interval']:-1]) * 8 * (
+                    1.024**2)
+        except:
+            self.log.warning(
+                'ValueError: Cannot get iperf result. Setting to 0')
+            current_throughput = 0
+        return current_throughput
+
+    def _test_nr_throughput_bler(self, testcase_params):
+        """Test function to run cellular throughput and BLER measurements.
+
+        The function runs BLER/throughput measurement after configuring the
+        callbox and DUT. The test supports running PHY or TCP/UDP layer traffic
+        in a variety of band/carrier/mcs/etc configurations.
+
+        Args:
+            testcase_params: dict containing test-specific parameters
+        Returns:
+            result: dict containing throughput results and meta data
+        """
+        testcase_params = self.compile_test_params(testcase_params)
+        testcase_results = collections.OrderedDict()
+        testcase_results['testcase_params'] = testcase_params
+        testcase_results['results'] = []
+        # Setup tester and wait for DUT to connect
+        self.setup_tester(testcase_params)
+        # Run test
+        stop_counter = 0
+        for cell_power in testcase_params['cell_power_list']:
+            result = collections.OrderedDict()
+            result['cell_power'] = cell_power
+            # Set DL cell power
+            for cell in testcase_params['dl_cell_list']:
+                self.keysight_test_app.set_cell_dl_power(
+                    'NR5G', cell, result['cell_power'], 1)
+            self.keysight_test_app.select_display_tab(
+                'NR5G', testcase_params['dl_cell_list'][0], 'BTHR', 'OTAGRAPH')
+            time.sleep(SHORT_SLEEP)
+            # Start BLER and throughput measurements
+            self.keysight_test_app.start_bler_measurement(
+                'NR5G', testcase_params['dl_cell_list'],
+                testcase_params['bler_measurement_length'])
+            if self.testclass_params['traffic_type'] != 'PHY':
+                result['iperf_throughput'] = self.run_iperf_traffic(
+                    testcase_params)
+            if self.testclass_params['log_power_metrics']:
+                if testcase_params[
+                        'bler_measurement_length'] >= 5000 and self.testclass_params[
+                            'traffic_type'] == 'PHY':
+                    time.sleep(testcase_params['bler_measurement_length'] /
+                               1000 - 5)
+                    cputils.log_system_power_metrics(self.dut, verbose=0)
+                else:
+                    self.log.warning('Test too short to log metrics')
+
+            result['bler_result'] = self.keysight_test_app.get_bler_result(
+                'NR5G', testcase_params['dl_cell_list'],
+                testcase_params['bler_measurement_length'])
+            result['tput_result'] = self.keysight_test_app.get_throughput(
+                'NR5G', testcase_params['dl_cell_list'])
+
+            # Print Test Summary
+            self.log.info("Cell Power: {}dBm".format(cell_power))
+            self.log.info(
+                "DL PHY Tput (Mbps):\tMin: {:.2f},\tAvg: {:.2f},\tMax: {:.2f},\tTheoretical: {:.2f}"
+                .format(
+                    result['tput_result']['total']['DL']['min_tput'],
+                    result['tput_result']['total']['DL']['average_tput'],
+                    result['tput_result']['total']['DL']['max_tput'],
+                    result['tput_result']['total']['DL']['theoretical_tput']))
+            self.log.info(
+                "UL PHY Tput (Mbps):\tMin: {:.2f},\tAvg: {:.2f},\tMax: {:.2f},\tTheoretical: {:.2f}"
+                .format(
+                    result['tput_result']['total']['UL']['min_tput'],
+                    result['tput_result']['total']['UL']['average_tput'],
+                    result['tput_result']['total']['UL']['max_tput'],
+                    result['tput_result']['total']['UL']['theoretical_tput']))
+            self.log.info("DL BLER: {:.2f}%\tUL BLER: {:.2f}%".format(
+                result['bler_result']['total']['DL']['nack_ratio'] * 100,
+                result['bler_result']['total']['UL']['nack_ratio'] * 100))
+            testcase_results['results'].append(result)
+            if self.testclass_params['traffic_type'] != 'PHY':
+                self.log.info("{} {} Tput: {:.2f} Mbps".format(
+                    self.testclass_params['traffic_type'],
+                    testcase_params['traffic_direction'],
+                    result['iperf_throughput']))
+
+            if result['bler_result']['total']['DL']['nack_ratio'] * 100 > 99:
+                stop_counter = stop_counter + 1
+            else:
+                stop_counter = 0
+            if stop_counter == STOP_COUNTER_LIMIT:
+                break
+        # Turn off NR cells
+        for cell in testcase_params['dl_cell_list'][::-1]:
+            self.keysight_test_app.set_cell_state('NR5G', cell, 0)
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+
+        # Save results
+        self.testclass_results[self.current_test_name] = testcase_results
+
+    def compile_test_params(self, testcase_params):
+        """Function that completes all test params based on the test name.
+
+        Args:
+            testcase_params: dict containing test-specific parameters
+        """
+        testcase_params['bler_measurement_length'] = int(
+            self.testclass_params['traffic_duration'] / SUBFRAME_LENGTH)
+        testcase_params['cell_power_list'] = numpy.arange(
+            self.testclass_params['cell_power_start'],
+            self.testclass_params['cell_power_stop'],
+            self.testclass_params['cell_power_step'])
+        if self.testclass_params['traffic_type'] == 'PHY':
+            return testcase_params
+        if self.testclass_params['traffic_type'] == 'TCP':
+            testcase_params['iperf_socket_size'] = self.testclass_params.get(
+                'tcp_socket_size', None)
+            testcase_params['iperf_processes'] = self.testclass_params.get(
+                'tcp_processes', 1)
+        elif self.testclass_params['traffic_type'] == 'UDP':
+            testcase_params['iperf_socket_size'] = self.testclass_params.get(
+                'udp_socket_size', None)
+            testcase_params['iperf_processes'] = self.testclass_params.get(
+                'udp_processes', 1)
+        if (testcase_params['traffic_direction'] == 'DL'
+                and not isinstance(self.iperf_server, ipf.IPerfServerOverAdb)
+            ) or (testcase_params['traffic_direction'] == 'UL'
+                  and isinstance(self.iperf_server, ipf.IPerfServerOverAdb)):
+            testcase_params['iperf_args'] = wputils.get_iperf_arg_string(
+                duration=self.testclass_params['traffic_duration'],
+                reverse_direction=1,
+                traffic_type=self.testclass_params['traffic_type'],
+                socket_size=testcase_params['iperf_socket_size'],
+                num_processes=testcase_params['iperf_processes'],
+                udp_throughput=self.testclass_params['UDP_rates'].get(
+                    testcase_params['num_dl_cells'],
+                    self.testclass_params['UDP_rates']["default"]),
+                udp_length=1440)
+            testcase_params['use_client_output'] = True
+        elif (testcase_params['traffic_direction'] == 'UL'
+              and not isinstance(self.iperf_server, ipf.IPerfServerOverAdb)
+              ) or (testcase_params['traffic_direction'] == 'DL'
+                    and isinstance(self.iperf_server, ipf.IPerfServerOverAdb)):
+            testcase_params['iperf_args'] = wputils.get_iperf_arg_string(
+                duration=self.testclass_params['traffic_duration'],
+                reverse_direction=0,
+                traffic_type=self.testclass_params['traffic_type'],
+                socket_size=testcase_params['iperf_socket_size'],
+                num_processes=testcase_params['iperf_processes'],
+                udp_throughput=self.testclass_params['UDP_rates'].get(
+                    testcase_params['num_dl_cells'],
+                    self.testclass_params['UDP_rates']["default"]),
+                udp_length=1440)
+            testcase_params['use_client_output'] = False
+        return testcase_params
+
+    def generate_test_cases(self, bands, channels, mcs_pair_list,
+                            num_dl_cells_list, num_ul_cells_list,
+                            dl_mimo_config, ul_mimo_config, **kwargs):
+        """Function that auto-generates test cases for a test class."""
+        test_cases = ['test_load_scpi']
+
+        for band, channel, num_ul_cells, num_dl_cells, mcs_pair in itertools.product(
+                bands, channels, num_ul_cells_list, num_dl_cells_list,
+                mcs_pair_list):
+            if num_ul_cells > num_dl_cells:
+                continue
+            if channel not in cputils.PCC_PRESET_MAPPING[band]:
+                continue
+            test_name = 'test_nr_throughput_bler_{}_{}_DL_{}CC_mcs{}_{}_UL_{}CC_mcs{}_{}'.format(
+                band, channel, num_dl_cells, mcs_pair[0], dl_mimo_config,
+                num_ul_cells, mcs_pair[1], ul_mimo_config)
+            test_params = collections.OrderedDict(
+                band=band,
+                channel=channel,
+                dl_mcs=mcs_pair[0],
+                ul_mcs=mcs_pair[1],
+                num_dl_cells=num_dl_cells,
+                num_ul_cells=num_ul_cells,
+                dl_mimo_config=dl_mimo_config,
+                ul_mimo_config=ul_mimo_config,
+                dl_cell_list=list(range(1, num_dl_cells + 1)),
+                ul_cell_list=list(range(1, num_ul_cells + 1)),
+                **kwargs)
+            setattr(self, test_name,
+                    partial(self._test_nr_throughput_bler, test_params))
+            test_cases.append(test_name)
+        return test_cases
+
+
+class Cellular5GFR2_DL_ThroughputTest(Cellular5GFR2ThroughputTest):
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                              ['low', 'mid', 'high'],
+                                              [(16, 4), (27, 4)],
+                                              list(range(1, 9)),
+                                              list(range(1, 3)),
+                                              dl_mimo_config='N2X2',
+                                              ul_mimo_config='N1X1',
+                                              schedule_scenario="FULL_TPUT",
+                                              traffic_direction='DL',
+                                              transform_precoding=0)
+
+
+class Cellular5GFR2_CP_UL_ThroughputTest(Cellular5GFR2ThroughputTest):
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                              ['low', 'mid', 'high'],
+                                              [(4, 16), (4, 27)], [1], [1],
+                                              dl_mimo_config='N2X2',
+                                              ul_mimo_config='N1X1',
+                                              schedule_scenario="FULL_TPUT",
+                                              traffic_direction='UL',
+                                              transform_precoding=0)
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [1], [1],
+                                     dl_mimo_config='N2X2',
+                                     ul_mimo_config='N2X2',
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=0))
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [2], [2],
+                                     dl_mimo_config='N2X2',
+                                     ul_mimo_config='N2X2',
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=0))
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [3], [3],
+                                     dl_mimo_config='N2X2',
+                                     ul_mimo_config='N2X2',
+                                     schedule_scenario="UL_RMC",
+                                     traffic_direction='UL',
+                                     transform_precoding=0))
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [4], [4],
+                                     dl_mimo_config='N2X2',
+                                     ul_mimo_config='N2X2',
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=0))
+
+
+class Cellular5GFR2_DFTS_UL_ThroughputTest(Cellular5GFR2ThroughputTest):
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                              ['low', 'mid', 'high'],
+                                              [(4, 16), (4, 27)], [1], [1],
+                                              dl_mimo_config='N2X2',
+                                              ul_mimo_config='N1X1',
+                                              schedule_scenario="FULL_TPUT",
+                                              traffic_direction='UL',
+                                              transform_precoding=1)
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [1], [1],
+                                     dl_mimo_config='N2X2',
+                                     ul_mimo_config='N2X2',
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=1))
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [2], [2],
+                                     dl_mimo_config='N2X2',
+                                     ul_mimo_config='N2X2',
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=1))
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [3], [3],
+                                     dl_mimo_config='N2X2',
+                                     ul_mimo_config='N2X2',
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=1))
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [4], [4],
+                                     dl_mimo_config='N2X2',
+                                     ul_mimo_config='N2X2',
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=1))
+
+
+class Cellular5GFR2_DL_FrequecySweep_ThroughputTest(Cellular5GFR2ThroughputTest
+                                                    ):
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        dl_frequency_sweep_params = self.user_params['throughput_test_params'][
+            'dl_frequency_sweep']
+        self.tests = self.generate_test_cases(dl_frequency_sweep_params,
+                                              [(16, 4), (27, 4)],
+                                              schedule_scenario="FULL_TPUT",
+                                              traffic_direction='DL',
+                                              transform_precoding=0,
+                                              dl_mimo_config='N2X2',
+                                              ul_mimo_config='N1X1')
+
+    def generate_test_cases(self, dl_frequency_sweep_params, mcs_pair_list,
+                            **kwargs):
+        """Function that auto-generates test cases for a test class."""
+        test_cases = ['test_load_scpi']
+
+        for band, band_config in dl_frequency_sweep_params.items():
+            for num_dl_cells_str, sweep_config in band_config.items():
+                num_dl_cells = int(num_dl_cells_str[0])
+                num_ul_cells = 1
+                freq_vector = numpy.arange(sweep_config[0], sweep_config[1],
+                                           sweep_config[2])
+                for freq in freq_vector:
+                    for mcs_pair in mcs_pair_list:
+                        test_name = 'test_nr_throughput_bler_{}_{}MHz_DL_{}CC_mcs{}_UL_{}CC_mcs{}'.format(
+                            band, freq, num_dl_cells, mcs_pair[0],
+                            num_ul_cells, mcs_pair[1])
+                        test_params = collections.OrderedDict(
+                            band=band,
+                            channel=freq,
+                            dl_mcs=mcs_pair[0],
+                            ul_mcs=mcs_pair[1],
+                            num_dl_cells=num_dl_cells,
+                            num_ul_cells=num_ul_cells,
+                            dl_cell_list=list(range(1, num_dl_cells + 1)),
+                            ul_cell_list=list(range(1, num_ul_cells + 1)),
+                            **kwargs)
+                        setattr(
+                            self, test_name,
+                            partial(self._test_nr_throughput_bler,
+                                    test_params))
+                        test_cases.append(test_name)
+        return test_cases
diff --git a/acts_tests/tests/google/cellular/performance/CellularFr1SensitivityTest.py b/acts_tests/tests/google/cellular/performance/CellularFr1SensitivityTest.py
new file mode 100644
index 0000000..e483e59
--- /dev/null
+++ b/acts_tests/tests/google/cellular/performance/CellularFr1SensitivityTest.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import csv
+import itertools
+import numpy
+import json
+import os
+from acts import context
+from acts import base_test
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi.wifi_performance_test_utils.bokeh_figure import BokehFigure
+from CellularLtePlusFr1PeakThroughputTest import CellularFr1SingleCellPeakThroughputTest
+
+from functools import partial
+
+
+class CellularFr1SensitivityTest(CellularFr1SingleCellPeakThroughputTest):
+    """Class to test single cell FR1 NSA sensitivity"""
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+        self.testclass_params = self.user_params['nr_sensitivity_test_params']
+        self.tests = self.generate_test_cases(
+            channel_list=['LOW', 'MID', 'HIGH'],
+            dl_mcs_list=list(numpy.arange(27, -1, -1)),
+            nr_ul_mcs=4,
+            lte_dl_mcs_table='QAM256',
+            lte_dl_mcs=4,
+            lte_ul_mcs_table='QAM256',
+            lte_ul_mcs=4,
+            transform_precoding=0)
+
+    def process_testclass_results(self):
+        # Plot individual test id results raw data and compile metrics
+        plots = collections.OrderedDict()
+        compiled_data = collections.OrderedDict()
+        for testcase_name, testcase_data in self.testclass_results.items():
+            cell_config = testcase_data['testcase_params'][
+                'endc_combo_config']['cell_list'][1]
+            test_id = tuple(('band', cell_config['band']))
+            if test_id not in plots:
+                # Initialize test id data when not present
+                compiled_data[test_id] = {
+                    'mcs': [],
+                    'average_throughput': [],
+                    'theoretical_throughput': [],
+                    'cell_power': [],
+                }
+                plots[test_id] = BokehFigure(
+                    title='Band {} - BLER Curves'.format(cell_config['band']),
+                    x_label='Cell Power (dBm)',
+                    primary_y_label='BLER (Mbps)')
+                test_id_rvr = test_id + tuple('RvR')
+                plots[test_id_rvr] = BokehFigure(
+                    title='Band {} - RvR'.format(cell_config['band']),
+                    x_label='Cell Power (dBm)',
+                    primary_y_label='PHY Rate (Mbps)')
+            # Compile test id data and metrics
+            compiled_data[test_id]['average_throughput'].append(
+                testcase_data['average_throughput_list'])
+            compiled_data[test_id]['cell_power'].append(
+                testcase_data['cell_power_list'])
+            compiled_data[test_id]['mcs'].append(
+                testcase_data['testcase_params']['nr_dl_mcs'])
+            # Add test id to plots
+            plots[test_id].add_line(
+                testcase_data['cell_power_list'],
+                testcase_data['bler_list'],
+                'MCS {}'.format(testcase_data['testcase_params']['nr_dl_mcs']),
+                width=1)
+            plots[test_id_rvr].add_line(
+                testcase_data['cell_power_list'],
+                testcase_data['average_throughput_list'],
+                'MCS {}'.format(testcase_data['testcase_params']['nr_dl_mcs']),
+                width=1,
+                style='dashed')
+
+        # Compute average RvRs and compute metrics over orientations
+        for test_id, test_data in compiled_data.items():
+            test_id_rvr = test_id + tuple('RvR')
+            cell_power_interp = sorted(set(sum(test_data['cell_power'], [])))
+            average_throughput_interp = []
+            for mcs, cell_power, throughput in zip(
+                    test_data['mcs'], test_data['cell_power'],
+                    test_data['average_throughput']):
+                throughput_interp = numpy.interp(cell_power_interp,
+                                                 cell_power[::-1],
+                                                 throughput[::-1])
+                average_throughput_interp.append(throughput_interp)
+            rvr = numpy.max(average_throughput_interp, 0)
+            plots[test_id_rvr].add_line(cell_power_interp, rvr,
+                                        'Rate vs. Range')
+
+        figure_list = []
+        for plot_id, plot in plots.items():
+            plot.generate_figure()
+            figure_list.append(plot)
+        output_file_path = os.path.join(self.log_path, 'results.html')
+        BokehFigure.save_figures(figure_list, output_file_path)
+
+    def process_testcase_results(self):
+        if self.current_test_name not in self.testclass_results:
+            return
+        testcase_data = self.testclass_results[self.current_test_name]
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            '{}.json'.format(self.current_test_name))
+        with open(results_file_path, 'w') as results_file:
+            json.dump(wputils.serialize_dict(testcase_data),
+                      results_file,
+                      indent=4)
+
+        bler_list = []
+        average_throughput_list = []
+        theoretical_throughput_list = []
+        cell_power_list = testcase_data['testcase_params']['cell_power_sweep'][
+            1]
+        for result in testcase_data['results']:
+            bler_list.append(
+                result['nr_bler_result']['total']['DL']['nack_ratio'])
+            average_throughput_list.append(
+                result['nr_tput_result']['total']['DL']['average_tput'])
+            theoretical_throughput_list.append(
+                result['nr_tput_result']['total']['DL']['theoretical_tput'])
+        padding_len = len(cell_power_list) - len(average_throughput_list)
+        average_throughput_list.extend([0] * padding_len)
+        theoretical_throughput_list.extend([0] * padding_len)
+
+        bler_above_threshold = [
+            bler > self.testclass_params['bler_threshold']
+            for bler in bler_list
+        ]
+        for idx in range(len(bler_above_threshold)):
+            if all(bler_above_threshold[idx:]):
+                sensitivity_idx = max(idx, 1) - 1
+                break
+        else:
+            sensitivity_idx = -1
+        sensitivity = cell_power_list[sensitivity_idx]
+        self.log.info('NR Band {} MCS {} Sensitivity = {}dBm'.format(
+            testcase_data['testcase_params']['endc_combo_config']['cell_list']
+            [1]['band'], testcase_data['testcase_params']['nr_dl_mcs'],
+            sensitivity))
+
+        testcase_data['bler_list'] = bler_list
+        testcase_data['average_throughput_list'] = average_throughput_list
+        testcase_data[
+            'theoretical_throughput_list'] = theoretical_throughput_list
+        testcase_data['cell_power_list'] = cell_power_list
+        testcase_data['sensitivity'] = sensitivity
+
+    def get_per_cell_power_sweeps(self, testcase_params):
+        # get reference test
+        current_band = testcase_params['endc_combo_config']['cell_list'][1][
+            'band']
+        reference_test = None
+        reference_sensitivity = None
+        for testcase_name, testcase_data in self.testclass_results.items():
+            if testcase_data['testcase_params']['endc_combo_config'][
+                    'cell_list'][1]['band'] == current_band:
+                reference_test = testcase_name
+                reference_sensitivity = testcase_data['sensitivity']
+        if reference_test and reference_sensitivity and not self.retry_flag:
+            start_atten = reference_sensitivity + self.testclass_params[
+                'adjacent_mcs_gap']
+            self.log.info(
+                "Reference test {} found. Sensitivity {} dBm. Starting at {} dBm"
+                .format(reference_test, reference_sensitivity, start_atten))
+        else:
+            start_atten = self.testclass_params['nr_cell_power_start']
+            self.log.info(
+                "Reference test not found. Starting at {} dBm".format(
+                    start_atten))
+        # get current cell power start
+        nr_cell_sweep = list(
+            numpy.arange(start_atten,
+                         self.testclass_params['nr_cell_power_stop'],
+                         self.testclass_params['nr_cell_power_step']))
+        lte_sweep = [self.testclass_params['lte_cell_power']
+                     ] * len(nr_cell_sweep)
+        cell_power_sweeps = [lte_sweep, nr_cell_sweep]
+        return cell_power_sweeps
+
+    def generate_test_cases(self, channel_list, dl_mcs_list, **kwargs):
+        test_cases = []
+        with open(self.testclass_params['nr_single_cell_configs'],
+                  'r') as csvfile:
+            test_configs = csv.DictReader(csvfile)
+            for test_config, channel, nr_dl_mcs in itertools.product(
+                    test_configs, channel_list, dl_mcs_list):
+                if int(test_config['skip_test']):
+                    continue
+                endc_combo_config = self.generate_endc_combo_config(
+                    test_config)
+                test_name = 'test_fr1_{}_{}_dl_mcs{}'.format(
+                    test_config['nr_band'], channel.lower(), nr_dl_mcs)
+                test_params = collections.OrderedDict(
+                    endc_combo_config=endc_combo_config,
+                    nr_dl_mcs=nr_dl_mcs,
+                    **kwargs)
+                setattr(self, test_name,
+                        partial(self._test_throughput_bler, test_params))
+                test_cases.append(test_name)
+        return test_cases
diff --git a/acts_tests/tests/google/cellular/performance/CellularFr2PeakThroughputTest.py b/acts_tests/tests/google/cellular/performance/CellularFr2PeakThroughputTest.py
new file mode 100644
index 0000000..848832e
--- /dev/null
+++ b/acts_tests/tests/google/cellular/performance/CellularFr2PeakThroughputTest.py
@@ -0,0 +1,602 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import csv
+import itertools
+import json
+import re
+import numpy
+import os
+from acts import context
+from acts import base_test
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts_contrib.test_utils.cellular.performance import cellular_performance_test_utils as cputils
+from acts_contrib.test_utils.cellular.performance.CellularThroughputBaseTest import CellularThroughputBaseTest
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+
+from functools import partial
+
+LONG_SLEEP = 10
+MEDIUM_SLEEP = 2
+IPERF_TIMEOUT = 10
+SHORT_SLEEP = 1
+SUBFRAME_LENGTH = 0.001
+STOP_COUNTER_LIMIT = 3
+
+
+class CellularFr2PeakThroughputTest(CellularThroughputBaseTest):
+    """Base class to test cellular FR2 throughput
+
+    This class implements cellular FR2 throughput tests on a callbox setup.
+    The class setups up the callbox in the desired configurations, configures
+    and connects the phone, and runs traffic/iperf throughput.
+    """
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+
+    def process_testcase_results(self):
+        """Publish test case metrics and save results"""
+        if self.current_test_name not in self.testclass_results:
+            return
+        testcase_data = self.testclass_results[self.current_test_name]
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            '{}.json'.format(self.current_test_name))
+        with open(results_file_path, 'w') as results_file:
+            json.dump(wputils.serialize_dict(testcase_data),
+                      results_file,
+                      indent=4)
+        testcase_result = testcase_data['results'][0]
+        metric_map = {
+            'tcp_udp_tput': testcase_result.get('iperf_throughput',
+                                                float('nan'))
+        }
+        if testcase_data['testcase_params']['endc_combo_config'][
+                'nr_cell_count']:
+            metric_map.update({
+                'nr_min_dl_tput':
+                testcase_result['nr_tput_result']['total']['DL']['min_tput'],
+                'nr_max_dl_tput':
+                testcase_result['nr_tput_result']['total']['DL']['max_tput'],
+                'nr_avg_dl_tput':
+                testcase_result['nr_tput_result']['total']['DL']
+                ['average_tput'],
+                'nr_theoretical_dl_tput':
+                testcase_result['nr_tput_result']['total']['DL']
+                ['theoretical_tput'],
+                'nr_dl_bler':
+                testcase_result['nr_bler_result']['total']['DL']['nack_ratio']
+                * 100,
+                'nr_min_dl_tput':
+                testcase_result['nr_tput_result']['total']['UL']['min_tput'],
+                'nr_max_dl_tput':
+                testcase_result['nr_tput_result']['total']['UL']['max_tput'],
+                'nr_avg_dl_tput':
+                testcase_result['nr_tput_result']['total']['UL']
+                ['average_tput'],
+                'nr_theoretical_dl_tput':
+                testcase_result['nr_tput_result']['total']['UL']
+                ['theoretical_tput'],
+                'nr_ul_bler':
+                testcase_result['nr_bler_result']['total']['UL']['nack_ratio']
+                * 100
+            })
+        if testcase_data['testcase_params']['endc_combo_config'][
+                'lte_cell_count']:
+            metric_map.update({
+                'lte_min_dl_tput':
+                testcase_result['lte_tput_result']['total']['DL']['min_tput'],
+                'lte_max_dl_tput':
+                testcase_result['lte_tput_result']['total']['DL']['max_tput'],
+                'lte_avg_dl_tput':
+                testcase_result['lte_tput_result']['total']['DL']
+                ['average_tput'],
+                'lte_theoretical_dl_tput':
+                testcase_result['lte_tput_result']['total']['DL']
+                ['theoretical_tput'],
+                'lte_dl_bler':
+                testcase_result['lte_bler_result']['total']['DL']['nack_ratio']
+                * 100,
+                'lte_min_dl_tput':
+                testcase_result['lte_tput_result']['total']['UL']['min_tput'],
+                'lte_max_dl_tput':
+                testcase_result['lte_tput_result']['total']['UL']['max_tput'],
+                'lte_avg_dl_tput':
+                testcase_result['lte_tput_result']['total']['UL']
+                ['average_tput'],
+                'lte_theoretical_dl_tput':
+                testcase_result['lte_tput_result']['total']['UL']
+                ['theoretical_tput'],
+                'lte_ul_bler':
+                testcase_result['lte_bler_result']['total']['UL']['nack_ratio']
+                * 100
+            })
+        if self.publish_testcase_metrics:
+            for metric_name, metric_value in metric_map.items():
+                self.testcase_metric_logger.add_metric(metric_name,
+                                                       metric_value)
+
+    def process_testclass_results(self):
+        """Saves CSV with all test results to enable comparison."""
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            'results.csv')
+        with open(results_file_path, 'w', newline='') as csvfile:
+            field_names = [
+                'Test Name', 'NR DL Min. Throughput', 'NR DL Max. Throughput',
+                'NR DL Avg. Throughput', 'NR DL Theoretical Throughput',
+                'NR UL Min. Throughput', 'NR UL Max. Throughput',
+                'NR UL Avg. Throughput', 'NR UL Theoretical Throughput',
+                'NR DL BLER (%)', 'NR UL BLER (%)', 'LTE DL Min. Throughput',
+                'LTE DL Max. Throughput', 'LTE DL Avg. Throughput',
+                'LTE DL Theoretical Throughput', 'LTE UL Min. Throughput',
+                'LTE UL Max. Throughput', 'LTE UL Avg. Throughput',
+                'LTE UL Theoretical Throughput', 'LTE DL BLER (%)',
+                'LTE UL BLER (%)', 'TCP/UDP Throughput'
+            ]
+            writer = csv.DictWriter(csvfile, fieldnames=field_names)
+            writer.writeheader()
+
+            for testcase_name, testcase_results in self.testclass_results.items(
+            ):
+                for result in testcase_results['results']:
+                    row_dict = {
+                        'Test Name': testcase_name,
+                        'TCP/UDP Throughput':
+                        result.get('iperf_throughput', 0)
+                    }
+                    if testcase_results['testcase_params'][
+                            'endc_combo_config']['nr_cell_count']:
+                        row_dict.update({
+                            'NR DL Min. Throughput':
+                            result['nr_tput_result']['total']['DL']
+                            ['min_tput'],
+                            'NR DL Max. Throughput':
+                            result['nr_tput_result']['total']['DL']
+                            ['max_tput'],
+                            'NR DL Avg. Throughput':
+                            result['nr_tput_result']['total']['DL']
+                            ['average_tput'],
+                            'NR DL Theoretical Throughput':
+                            result['nr_tput_result']['total']['DL']
+                            ['theoretical_tput'],
+                            'NR UL Min. Throughput':
+                            result['nr_tput_result']['total']['UL']
+                            ['min_tput'],
+                            'NR UL Max. Throughput':
+                            result['nr_tput_result']['total']['UL']
+                            ['max_tput'],
+                            'NR UL Avg. Throughput':
+                            result['nr_tput_result']['total']['UL']
+                            ['average_tput'],
+                            'NR UL Theoretical Throughput':
+                            result['nr_tput_result']['total']['UL']
+                            ['theoretical_tput'],
+                            'NR DL BLER (%)':
+                            result['nr_bler_result']['total']['DL']
+                            ['nack_ratio'] * 100,
+                            'NR UL BLER (%)':
+                            result['nr_bler_result']['total']['UL']
+                            ['nack_ratio'] * 100
+                        })
+                    if testcase_results['testcase_params'][
+                            'endc_combo_config']['lte_cell_count']:
+                        row_dict.update({
+                            'LTE DL Min. Throughput':
+                            result['lte_tput_result']['total']['DL']
+                            ['min_tput'],
+                            'LTE DL Max. Throughput':
+                            result['lte_tput_result']['total']['DL']
+                            ['max_tput'],
+                            'LTE DL Avg. Throughput':
+                            result['lte_tput_result']['total']['DL']
+                            ['average_tput'],
+                            'LTE DL Theoretical Throughput':
+                            result['lte_tput_result']['total']['DL']
+                            ['theoretical_tput'],
+                            'LTE UL Min. Throughput':
+                            result['lte_tput_result']['total']['UL']
+                            ['min_tput'],
+                            'LTE UL Max. Throughput':
+                            result['lte_tput_result']['total']['UL']
+                            ['max_tput'],
+                            'LTE UL Avg. Throughput':
+                            result['lte_tput_result']['total']['UL']
+                            ['average_tput'],
+                            'LTE UL Theoretical Throughput':
+                            result['lte_tput_result']['total']['UL']
+                            ['theoretical_tput'],
+                            'LTE DL BLER (%)':
+                            result['lte_bler_result']['total']['DL']
+                            ['nack_ratio'] * 100,
+                            'LTE UL BLER (%)':
+                            result['lte_bler_result']['total']['UL']
+                            ['nack_ratio'] * 100
+                        })
+                    writer.writerow(row_dict)
+
+    def get_per_cell_power_sweeps(self, testcase_params):
+        """Function to get per cell power sweep lists
+
+        Args:
+            testcase_params: dict containing all test case params
+        Returns:
+            cell_power_sweeps: list of cell power sweeps for each cell under test
+        """
+        cell_power_sweeps = []
+        for cell in testcase_params['endc_combo_config']['cell_list']:
+            if cell['cell_type'] == 'LTE':
+                sweep = [self.testclass_params['lte_cell_power']]
+            else:
+                sweep = [self.testclass_params['nr_cell_power']]
+            cell_power_sweeps.append(sweep)
+        return cell_power_sweeps
+
+    def generate_endc_combo_config(self, test_config):
+        """Function to generate ENDC combo config from CSV test config
+
+        Args:
+            test_config: dict containing ENDC combo config from CSV
+        Returns:
+            endc_combo_config: dictionary with all ENDC combo settings
+        """
+        endc_combo_config = collections.OrderedDict()
+        cell_config_list = []
+
+        lte_cell_count = 1
+        lte_carriers = [1]
+        lte_scc_list = []
+        endc_combo_config['lte_pcc'] = 1
+        lte_cell = {
+            'cell_type':
+            'LTE',
+            'cell_number':
+            1,
+            'pcc':
+            1,
+            'band':
+            test_config['lte_band'],
+            'dl_bandwidth':
+            test_config['lte_bandwidth'],
+            'ul_enabled':
+            1,
+            'duplex_mode':
+            test_config['lte_duplex_mode'],
+            'dl_mimo_config':
+            'D{nss}U{nss}'.format(nss=test_config['lte_dl_mimo_config']),
+            'ul_mimo_config':
+            'D{nss}U{nss}'.format(nss=test_config['lte_ul_mimo_config']),
+            'transmission_mode':
+            'TM1'
+        }
+        cell_config_list.append(lte_cell)
+
+        nr_cell_count = 0
+        nr_dl_carriers = []
+        nr_ul_carriers = []
+        for nr_cell_idx in range(1, test_config['num_dl_cells'] + 1):
+            nr_cell = {
+                'cell_type':
+                'NR5G',
+                'cell_number':
+                nr_cell_idx,
+                'band':
+                test_config['nr_band'],
+                'duplex_mode':
+                test_config['nr_duplex_mode'],
+                'channel':
+                test_config['nr_channel'],
+                'dl_mimo_config':
+                'N{nss}X{nss}'.format(nss=test_config['nr_dl_mimo_config']),
+                'dl_bandwidth_class':
+                'A',
+                'dl_bandwidth':
+                test_config['nr_bandwidth'],
+                'ul_enabled':
+                1 if nr_cell_idx <= test_config['num_ul_cells'] else 0,
+                'ul_bandwidth_class':
+                'A',
+                'ul_mimo_config':
+                'N{nss}X{nss}'.format(nss=test_config['nr_ul_mimo_config']),
+                'subcarrier_spacing':
+                'MU3'
+            }
+            cell_config_list.append(nr_cell)
+            nr_cell_count = nr_cell_count + 1
+            nr_dl_carriers.append(nr_cell_idx)
+            if nr_cell_idx <= test_config['num_ul_cells']:
+                nr_ul_carriers.append(nr_cell_idx)
+
+        endc_combo_config['lte_cell_count'] = lte_cell_count
+        endc_combo_config['nr_cell_count'] = nr_cell_count
+        endc_combo_config['nr_dl_carriers'] = nr_dl_carriers
+        endc_combo_config['nr_ul_carriers'] = nr_ul_carriers
+        endc_combo_config['cell_list'] = cell_config_list
+        endc_combo_config['lte_scc_list'] = lte_scc_list
+        endc_combo_config['lte_carriers'] = lte_carriers
+        return endc_combo_config
+
+    def generate_test_cases(self, bands, channels, nr_mcs_pair_list,
+                            num_dl_cells_list, num_ul_cells_list,
+                            dl_mimo_config, ul_mimo_config, **kwargs):
+        """Function that auto-generates test cases for a test class."""
+        test_cases = []
+        for band, channel, num_ul_cells, num_dl_cells, nr_mcs_pair in itertools.product(
+                bands, channels, num_ul_cells_list, num_dl_cells_list,
+                nr_mcs_pair_list):
+            if num_ul_cells > num_dl_cells:
+                continue
+            if channel not in cputils.PCC_PRESET_MAPPING[band]:
+                continue
+            test_config = {
+                'lte_band': 2,
+                'lte_bandwidth': 'BW20',
+                'lte_duplex_mode': 'FDD',
+                'lte_dl_mimo_config': 1,
+                'lte_ul_mimo_config': 1,
+                'nr_band': band,
+                'nr_bandwidth': 'BW100',
+                'nr_duplex_mode': 'TDD',
+                'nr_channel': channel,
+                'num_dl_cells': num_dl_cells,
+                'num_ul_cells': num_ul_cells,
+                'nr_dl_mimo_config': dl_mimo_config,
+                'nr_ul_mimo_config': ul_mimo_config
+            }
+            endc_combo_config = self.generate_endc_combo_config(test_config)
+            test_name = 'test_fr2_{}_{}_DL_{}CC_mcs{}_{}x{}_UL_{}CC_mcs{}_{}x{}'.format(
+                band, channel, num_dl_cells, nr_mcs_pair[0], dl_mimo_config,
+                dl_mimo_config, num_ul_cells, nr_mcs_pair[1], ul_mimo_config,
+                ul_mimo_config)
+            test_params = collections.OrderedDict(
+                endc_combo_config=endc_combo_config,
+                nr_dl_mcs=nr_mcs_pair[0],
+                nr_ul_mcs=nr_mcs_pair[1],
+                **kwargs)
+            setattr(self, test_name,
+                    partial(self._test_throughput_bler, test_params))
+            test_cases.append(test_name)
+        return test_cases
+
+
+class CellularFr2DlPeakThroughputTest(CellularFr2PeakThroughputTest):
+    """Base class to test cellular FR2 throughput
+
+    This class implements cellular FR2 throughput tests on a callbox setup.
+    The class setups up the callbox in the desired configurations, configures
+    and connects the phone, and runs traffic/iperf throughput.
+    """
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.testclass_params = self.user_params['throughput_test_params']
+        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                              ['low', 'mid', 'high'],
+                                              [(16, 4), (27, 4)],
+                                              list(range(1, 9)),
+                                              list(range(1, 3)),
+                                              force_contiguous_nr_channel=True,
+                                              dl_mimo_config=2,
+                                              ul_mimo_config=1,
+                                              schedule_scenario="FULL_TPUT",
+                                              traffic_direction='DL',
+                                              transform_precoding=0,
+                                              lte_dl_mcs=4,
+                                              lte_dl_mcs_table='QAM256',
+                                              lte_ul_mcs=4,
+                                              lte_ul_mcs_table='QAM64')
+
+
+class CellularFr2CpOfdmUlPeakThroughputTest(CellularFr2PeakThroughputTest):
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.testclass_params = self.user_params['throughput_test_params']
+        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                              ['low', 'mid', 'high'],
+                                              [(4, 16), (4, 27)], [1], [1],
+                                              force_contiguous_nr_channel=True,
+                                              dl_mimo_config=2,
+                                              ul_mimo_config=1,
+                                              schedule_scenario="FULL_TPUT",
+                                              traffic_direction='UL',
+                                              transform_precoding=0,
+                                              lte_dl_mcs=4,
+                                              lte_dl_mcs_table='QAM256',
+                                              lte_ul_mcs=4,
+                                              lte_ul_mcs_table='QAM64')
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [1], [1],
+                                     force_contiguous_nr_channel=True,
+                                     dl_mimo_config=2,
+                                     ul_mimo_config=2,
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=0,
+                                     lte_dl_mcs=4,
+                                     lte_dl_mcs_table='QAM256',
+                                     lte_ul_mcs=4,
+                                     lte_ul_mcs_table='QAM64'))
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [2], [2],
+                                     force_contiguous_nr_channel=True,
+                                     dl_mimo_config=2,
+                                     ul_mimo_config=2,
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=0,
+                                     lte_dl_mcs=4,
+                                     lte_dl_mcs_table='QAM256',
+                                     lte_ul_mcs=4,
+                                     lte_ul_mcs_table='QAM64'))
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [4], [4],
+                                     force_contiguous_nr_channel=True,
+                                     dl_mimo_config=2,
+                                     ul_mimo_config=2,
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=0,
+                                     lte_dl_mcs=4,
+                                     lte_dl_mcs_table='QAM256',
+                                     lte_ul_mcs=4,
+                                     lte_ul_mcs_table='QAM64'))
+
+
+class CellularFr2DftsOfdmUlPeakThroughputTest(CellularFr2PeakThroughputTest):
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.testclass_params = self.user_params['throughput_test_params']
+        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                              ['low', 'mid', 'high'],
+                                              [(4, 16), (4, 27)], [1], [1],
+                                              force_contiguous_nr_channel=True,
+                                              dl_mimo_config=2,
+                                              ul_mimo_config=1,
+                                              schedule_scenario="FULL_TPUT",
+                                              traffic_direction='UL',
+                                              transform_precoding=1,
+                                              lte_dl_mcs=4,
+                                              lte_dl_mcs_table='QAM256',
+                                              lte_ul_mcs=4,
+                                              lte_ul_mcs_table='QAM64')
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [1], [1],
+                                     force_contiguous_nr_channel=True,
+                                     dl_mimo_config=2,
+                                     ul_mimo_config=2,
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=1,
+                                     lte_dl_mcs=4,
+                                     lte_dl_mcs_table='QAM256',
+                                     lte_ul_mcs=4,
+                                     lte_ul_mcs_table='QAM64'))
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [2], [2],
+                                     force_contiguous_nr_channel=True,
+                                     dl_mimo_config=2,
+                                     ul_mimo_config=2,
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=1,
+                                     lte_dl_mcs=4,
+                                     lte_dl_mcs_table='QAM256',
+                                     lte_ul_mcs=4,
+                                     lte_ul_mcs_table='QAM64'))
+        self.tests.extend(
+            self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                     ['low', 'mid', 'high'],
+                                     [(4, 16), (4, 27)], [4], [4],
+                                     force_contiguous_nr_channel=True,
+                                     dl_mimo_config=2,
+                                     ul_mimo_config=2,
+                                     schedule_scenario="FULL_TPUT",
+                                     traffic_direction='UL',
+                                     transform_precoding=1,
+                                     lte_dl_mcs=4,
+                                     lte_dl_mcs_table='QAM256',
+                                     lte_ul_mcs=4,
+                                     lte_ul_mcs_table='QAM64'))
+
+
+class CellularFr2DlFrequencySweepPeakThroughputTest(
+        CellularFr2PeakThroughputTest):
+    """Base class to test cellular FR2 throughput
+
+    This class implements cellular FR2 throughput tests on a callbox setup.
+    The class setups up the callbox in the desired configurations, configures
+    and connects the phone, and runs traffic/iperf throughput.
+    """
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.testclass_params = self.user_params['throughput_test_params']
+        self.tests = self.generate_test_cases(
+            ['N257', 'N258', 'N260', 'N261'],
+            self.user_params['throughput_test_params']['frequency_sweep'],
+            [(16, 4), (27, 4)],
+            force_contiguous_nr_channel=False,
+            dl_mimo_config=2,
+            ul_mimo_config=1,
+            schedule_scenario="FULL_TPUT",
+            traffic_direction='DL',
+            transform_precoding=0,
+            lte_dl_mcs=4,
+            lte_dl_mcs_table='QAM256',
+            lte_ul_mcs=4,
+            lte_ul_mcs_table='QAM64')
+
+    def generate_test_cases(self, bands, channels, nr_mcs_pair_list,
+                            num_dl_cells_list, num_ul_cells_list,
+                            dl_mimo_config, ul_mimo_config, **kwargs):
+        """Function that auto-generates test cases for a test class."""
+        test_cases = []
+        for band, channel, num_ul_cells, num_dl_cells, nr_mcs_pair in itertools.product(
+                bands, channels, num_ul_cells_list, num_dl_cells_list,
+                nr_mcs_pair_list):
+            if num_ul_cells > num_dl_cells:
+                continue
+            if channel not in cputils.PCC_PRESET_MAPPING[band]:
+                continue
+            test_config = {
+                'lte_band': 2,
+                'lte_bandwidth': 'BW20',
+                'lte_duplex_mode': 'FDD',
+                'lte_dl_mimo_config': 1,
+                'lte_ul_mimo_config': 1,
+                'nr_band': band,
+                'nr_bandwidth': 'BW100',
+                'nr_duplex_mode': 'TDD',
+                'nr_channel': channel,
+                'num_dl_cells': num_dl_cells,
+                'num_ul_cells': num_ul_cells,
+                'nr_dl_mimo_config': dl_mimo_config,
+                'nr_ul_mimo_config': ul_mimo_config
+            }
+            endc_combo_config = self.generate_endc_combo_config(test_config)
+            test_name = 'test_fr2_{}_{}_DL_{}CC_mcs{}_{}x{}_UL_{}CC_mcs{}_{}x{}'.format(
+                band, channel, num_dl_cells, nr_mcs_pair[0], dl_mimo_config,
+                dl_mimo_config, num_ul_cells, nr_mcs_pair[1], ul_mimo_config,
+                ul_mimo_config)
+            test_params = collections.OrderedDict(
+                endc_combo_config=endc_combo_config,
+                nr_dl_mcs=nr_mcs_pair[0],
+                nr_ul_mcs=nr_mcs_pair[1],
+                **kwargs)
+            setattr(self, test_name,
+                    partial(self._test_throughput_bler, test_params))
+            test_cases.append(test_name)
+        return test_cases
diff --git a/acts_tests/tests/google/cellular/performance/CellularFr2SensitivityTest.py b/acts_tests/tests/google/cellular/performance/CellularFr2SensitivityTest.py
new file mode 100644
index 0000000..4176a62
--- /dev/null
+++ b/acts_tests/tests/google/cellular/performance/CellularFr2SensitivityTest.py
@@ -0,0 +1,250 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import csv
+import itertools
+import numpy
+import json
+import os
+from acts import context
+from acts import base_test
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts_contrib.test_utils.cellular.performance import cellular_performance_test_utils as cputils
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi.wifi_performance_test_utils.bokeh_figure import BokehFigure
+from CellularFr2PeakThroughputTest import CellularFr2PeakThroughputTest
+
+from functools import partial
+
+
+class CellularFr2SensitivityTest(CellularFr2PeakThroughputTest):
+    """Class to test single cell FR1 NSA sensitivity"""
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+        self.testclass_params = self.user_params['nr_sensitivity_test_params']
+        self.log.info('Hello')
+        self.tests = self.generate_test_cases(
+            band_list=['N257', 'N258', 'N260', 'N261'],
+            channel_list=['low', 'mid', 'high'],
+            dl_mcs_list=list(numpy.arange(27, -1, -1)),
+            num_dl_cells_list=[1, 2, 4, 8],
+            dl_mimo_config=2,
+            nr_ul_mcs=4,
+            lte_dl_mcs_table='QAM256',
+            lte_dl_mcs=4,
+            lte_ul_mcs_table='QAM256',
+            lte_ul_mcs=4,
+            schedule_scenario="FULL_TPUT",
+            force_contiguous_nr_channel=True,
+            transform_precoding=0)
+
+    def process_testclass_results(self):
+        # Plot individual test id results raw data and compile metrics
+        plots = collections.OrderedDict()
+        compiled_data = collections.OrderedDict()
+        for testcase_name, testcase_data in self.testclass_results.items():
+            cell_config = testcase_data['testcase_params'][
+                'endc_combo_config']['cell_list'][1]
+            test_id = tuple(('band', cell_config['band']))
+            if test_id not in plots:
+                # Initialize test id data when not present
+                compiled_data[test_id] = {
+                    'mcs': [],
+                    'average_throughput': [],
+                    'theoretical_throughput': [],
+                    'cell_power': [],
+                }
+                plots[test_id] = BokehFigure(
+                    title='Band {} - BLER Curves'.format(cell_config['band']),
+                    x_label='Cell Power (dBm)',
+                    primary_y_label='BLER (Mbps)')
+                test_id_rvr = test_id + tuple('RvR')
+                plots[test_id_rvr] = BokehFigure(
+                    title='Band {} - RvR'.format(cell_config['band']),
+                    x_label='Cell Power (dBm)',
+                    primary_y_label='PHY Rate (Mbps)')
+            # Compile test id data and metrics
+            compiled_data[test_id]['average_throughput'].append(
+                testcase_data['average_throughput_list'])
+            compiled_data[test_id]['cell_power'].append(
+                testcase_data['cell_power_list'])
+            compiled_data[test_id]['mcs'].append(
+                testcase_data['testcase_params']['nr_dl_mcs'])
+            # Add test id to plots
+            plots[test_id].add_line(
+                testcase_data['cell_power_list'],
+                testcase_data['bler_list'],
+                'MCS {}'.format(testcase_data['testcase_params']['nr_dl_mcs']),
+                width=1)
+            plots[test_id_rvr].add_line(
+                testcase_data['cell_power_list'],
+                testcase_data['average_throughput_list'],
+                'MCS {}'.format(testcase_data['testcase_params']['nr_dl_mcs']),
+                width=1,
+                style='dashed')
+
+        # Compute average RvRs and compute metrics over orientations
+        for test_id, test_data in compiled_data.items():
+            test_id_rvr = test_id + tuple('RvR')
+            cell_power_interp = sorted(set(sum(test_data['cell_power'], [])))
+            average_throughput_interp = []
+            for mcs, cell_power, throughput in zip(
+                    test_data['mcs'], test_data['cell_power'],
+                    test_data['average_throughput']):
+                throughput_interp = numpy.interp(cell_power_interp,
+                                                 cell_power[::-1],
+                                                 throughput[::-1])
+                average_throughput_interp.append(throughput_interp)
+            rvr = numpy.max(average_throughput_interp, 0)
+            plots[test_id_rvr].add_line(cell_power_interp, rvr,
+                                        'Rate vs. Range')
+
+        figure_list = []
+        for plot_id, plot in plots.items():
+            plot.generate_figure()
+            figure_list.append(plot)
+        output_file_path = os.path.join(self.log_path, 'results.html')
+        BokehFigure.save_figures(figure_list, output_file_path)
+
+    def process_testcase_results(self):
+        if self.current_test_name not in self.testclass_results:
+            return
+        testcase_data = self.testclass_results[self.current_test_name]
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            '{}.json'.format(self.current_test_name))
+        with open(results_file_path, 'w') as results_file:
+            json.dump(wputils.serialize_dict(testcase_data),
+                      results_file,
+                      indent=4)
+
+        bler_list = []
+        average_throughput_list = []
+        theoretical_throughput_list = []
+        cell_power_list = testcase_data['testcase_params']['cell_power_sweep'][
+            1]
+        for result in testcase_data['results']:
+            bler_list.append(
+                result['nr_bler_result']['total']['DL']['nack_ratio'])
+            average_throughput_list.append(
+                result['nr_tput_result']['total']['DL']['average_tput'])
+            theoretical_throughput_list.append(
+                result['nr_tput_result']['total']['DL']['theoretical_tput'])
+        padding_len = len(cell_power_list) - len(average_throughput_list)
+        average_throughput_list.extend([0] * padding_len)
+        theoretical_throughput_list.extend([0] * padding_len)
+
+        bler_above_threshold = [
+            bler > self.testclass_params['bler_threshold']
+            for bler in bler_list
+        ]
+        for idx in range(len(bler_above_threshold)):
+            if all(bler_above_threshold[idx:]):
+                sensitivity_idx = max(idx, 1) - 1
+                break
+        else:
+            sensitivity_idx = -1
+        sensitivity = cell_power_list[sensitivity_idx]
+        self.log.info('NR Band {} MCS {} Sensitivity = {}dBm'.format(
+            testcase_data['testcase_params']['endc_combo_config']['cell_list']
+            [1]['band'], testcase_data['testcase_params']['nr_dl_mcs'],
+            sensitivity))
+
+        testcase_data['bler_list'] = bler_list
+        testcase_data['average_throughput_list'] = average_throughput_list
+        testcase_data[
+            'theoretical_throughput_list'] = theoretical_throughput_list
+        testcase_data['cell_power_list'] = cell_power_list
+        testcase_data['sensitivity'] = sensitivity
+
+    def get_per_cell_power_sweeps(self, testcase_params):
+        # get reference test
+        current_band = testcase_params['endc_combo_config']['cell_list'][1][
+            'band']
+        reference_test = None
+        reference_sensitivity = None
+        for testcase_name, testcase_data in self.testclass_results.items():
+            if testcase_data['testcase_params']['endc_combo_config'][
+                    'cell_list'][1]['band'] == current_band:
+                reference_test = testcase_name
+                reference_sensitivity = testcase_data['sensitivity']
+        if reference_test and reference_sensitivity and not self.retry_flag:
+            start_atten = reference_sensitivity + self.testclass_params[
+                'adjacent_mcs_gap']
+            self.log.info(
+                "Reference test {} found. Sensitivity {} dBm. Starting at {} dBm"
+                .format(reference_test, reference_sensitivity, start_atten))
+        else:
+            start_atten = self.testclass_params['nr_cell_power_start']
+            self.log.info(
+                "Reference test not found. Starting at {} dBm".format(
+                    start_atten))
+        # get current cell power start
+        nr_cell_sweep = list(
+            numpy.arange(start_atten,
+                         self.testclass_params['nr_cell_power_stop'],
+                         self.testclass_params['nr_cell_power_step']))
+        lte_sweep = [self.testclass_params['lte_cell_power']
+                     ] * len(nr_cell_sweep)
+        cell_power_sweeps = [lte_sweep]
+        cell_power_sweeps.extend(
+            [nr_cell_sweep] *
+            testcase_params['endc_combo_config']['nr_cell_count'])
+        return cell_power_sweeps
+
+    def generate_test_cases(self, band_list, channel_list, dl_mcs_list,
+                            num_dl_cells_list, dl_mimo_config, **kwargs):
+        """Function that auto-generates test cases for a test class."""
+        test_cases = []
+        for band, channel, num_dl_cells, nr_dl_mcs in itertools.product(
+                band_list, channel_list, num_dl_cells_list, dl_mcs_list):
+            if channel not in cputils.PCC_PRESET_MAPPING[band]:
+                continue
+            test_config = {
+                'lte_band': 2,
+                'lte_bandwidth': 'BW20',
+                'lte_duplex_mode': 'FDD',
+                'lte_dl_mimo_config': 1,
+                'lte_ul_mimo_config': 1,
+                'nr_band': band,
+                'nr_bandwidth': 'BW100',
+                'nr_duplex_mode': 'TDD',
+                'nr_channel': channel,
+                'num_dl_cells': num_dl_cells,
+                'num_ul_cells': 1,
+                'nr_dl_mimo_config': dl_mimo_config,
+                'nr_ul_mimo_config': 1
+            }
+            endc_combo_config = self.generate_endc_combo_config(test_config)
+            test_name = 'test_fr2_{}_{}_{}CC_mcs{}_{}x{}'.format(
+                band, channel.lower(), num_dl_cells, nr_dl_mcs, dl_mimo_config,
+                dl_mimo_config)
+            test_params = collections.OrderedDict(
+                endc_combo_config=endc_combo_config,
+                nr_dl_mcs=nr_dl_mcs,
+                **kwargs)
+            setattr(self, test_name,
+                    partial(self._test_throughput_bler, test_params))
+            test_cases.append(test_name)
+        self.log.info(test_cases)
+        return test_cases
diff --git a/acts_tests/tests/google/cellular/performance/CellularLtePlusFr1PeakThroughputTest.py b/acts_tests/tests/google/cellular/performance/CellularLtePlusFr1PeakThroughputTest.py
new file mode 100644
index 0000000..b422a30
--- /dev/null
+++ b/acts_tests/tests/google/cellular/performance/CellularLtePlusFr1PeakThroughputTest.py
@@ -0,0 +1,566 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import csv
+import itertools
+import json
+import re
+import os
+from acts import context
+from acts import base_test
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts_contrib.test_utils.cellular.performance import cellular_performance_test_utils as cputils
+from acts_contrib.test_utils.cellular.performance.CellularThroughputBaseTest import CellularThroughputBaseTest
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+
+from functools import partial
+
+LONG_SLEEP = 10
+MEDIUM_SLEEP = 2
+IPERF_TIMEOUT = 10
+SHORT_SLEEP = 1
+SUBFRAME_LENGTH = 0.001
+STOP_COUNTER_LIMIT = 3
+
+
+class CellularLtePlusFr1PeakThroughputTest(CellularThroughputBaseTest):
+    """Base class to test cellular LTE and FR1 throughput
+
+    This class implements cellular LTE & FR1 throughput tests on a callbox setup.
+    The class setups up the callbox in the desired configurations, configures
+    and connects the phone, and runs traffic/iperf throughput.
+    """
+
+    def process_testcase_results(self):
+        """Publish test case metrics and save results"""
+        if self.current_test_name not in self.testclass_results:
+            return
+        testcase_data = self.testclass_results[self.current_test_name]
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            '{}.json'.format(self.current_test_name))
+        with open(results_file_path, 'w') as results_file:
+            json.dump(wputils.serialize_dict(testcase_data),
+                      results_file,
+                      indent=4)
+        testcase_result = testcase_data['results'][0]
+        metric_map = {
+            'tcp_udp_tput': testcase_result.get('iperf_throughput',
+                                                float('nan'))
+        }
+        if testcase_data['testcase_params']['endc_combo_config'][
+                'nr_cell_count']:
+            metric_map.update({
+                'nr_min_dl_tput':
+                testcase_result['nr_tput_result']['total']['DL']['min_tput'],
+                'nr_max_dl_tput':
+                testcase_result['nr_tput_result']['total']['DL']['max_tput'],
+                'nr_avg_dl_tput':
+                testcase_result['nr_tput_result']['total']['DL']
+                ['average_tput'],
+                'nr_theoretical_dl_tput':
+                testcase_result['nr_tput_result']['total']['DL']
+                ['theoretical_tput'],
+                'nr_dl_bler':
+                testcase_result['nr_bler_result']['total']['DL']['nack_ratio']
+                * 100,
+                'nr_min_dl_tput':
+                testcase_result['nr_tput_result']['total']['UL']['min_tput'],
+                'nr_max_dl_tput':
+                testcase_result['nr_tput_result']['total']['UL']['max_tput'],
+                'nr_avg_dl_tput':
+                testcase_result['nr_tput_result']['total']['UL']
+                ['average_tput'],
+                'nr_theoretical_dl_tput':
+                testcase_result['nr_tput_result']['total']['UL']
+                ['theoretical_tput'],
+                'nr_ul_bler':
+                testcase_result['nr_bler_result']['total']['UL']['nack_ratio']
+                * 100
+            })
+        if testcase_data['testcase_params']['endc_combo_config'][
+                'lte_cell_count']:
+            metric_map.update({
+                'lte_min_dl_tput':
+                testcase_result['lte_tput_result']['total']['DL']['min_tput'],
+                'lte_max_dl_tput':
+                testcase_result['lte_tput_result']['total']['DL']['max_tput'],
+                'lte_avg_dl_tput':
+                testcase_result['lte_tput_result']['total']['DL']
+                ['average_tput'],
+                'lte_theoretical_dl_tput':
+                testcase_result['lte_tput_result']['total']['DL']
+                ['theoretical_tput'],
+                'lte_dl_bler':
+                testcase_result['lte_bler_result']['total']['DL']['nack_ratio']
+                * 100,
+                'lte_min_dl_tput':
+                testcase_result['lte_tput_result']['total']['UL']['min_tput'],
+                'lte_max_dl_tput':
+                testcase_result['lte_tput_result']['total']['UL']['max_tput'],
+                'lte_avg_dl_tput':
+                testcase_result['lte_tput_result']['total']['UL']
+                ['average_tput'],
+                'lte_theoretical_dl_tput':
+                testcase_result['lte_tput_result']['total']['UL']
+                ['theoretical_tput'],
+                'lte_ul_bler':
+                testcase_result['lte_bler_result']['total']['UL']['nack_ratio']
+                * 100
+            })
+        if self.publish_testcase_metrics:
+            for metric_name, metric_value in metric_map.items():
+                self.testcase_metric_logger.add_metric(metric_name,
+                                                       metric_value)
+
+    def process_testclass_results(self):
+        """Saves CSV with all test results to enable comparison."""
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            'results.csv')
+        with open(results_file_path, 'w', newline='') as csvfile:
+            field_names = [
+                'Test Name', 'NR DL Min. Throughput', 'NR DL Max. Throughput',
+                'NR DL Avg. Throughput', 'NR DL Theoretical Throughput',
+                'NR UL Min. Throughput', 'NR UL Max. Throughput',
+                'NR UL Avg. Throughput', 'NR UL Theoretical Throughput',
+                'NR DL BLER (%)', 'NR UL BLER (%)', 'LTE DL Min. Throughput',
+                'LTE DL Max. Throughput', 'LTE DL Avg. Throughput',
+                'LTE DL Theoretical Throughput', 'LTE UL Min. Throughput',
+                'LTE UL Max. Throughput', 'LTE UL Avg. Throughput',
+                'LTE UL Theoretical Throughput', 'LTE DL BLER (%)',
+                'LTE UL BLER (%)', 'TCP/UDP Throughput'
+            ]
+            writer = csv.DictWriter(csvfile, fieldnames=field_names)
+            writer.writeheader()
+
+            for testcase_name, testcase_results in self.testclass_results.items(
+            ):
+                for result in testcase_results['results']:
+                    row_dict = {
+                        'Test Name': testcase_name,
+                        'TCP/UDP Throughput':
+                        result.get('iperf_throughput', 0)
+                    }
+                    if testcase_results['testcase_params'][
+                            'endc_combo_config']['nr_cell_count']:
+                        row_dict.update({
+                            'NR DL Min. Throughput':
+                            result['nr_tput_result']['total']['DL']
+                            ['min_tput'],
+                            'NR DL Max. Throughput':
+                            result['nr_tput_result']['total']['DL']
+                            ['max_tput'],
+                            'NR DL Avg. Throughput':
+                            result['nr_tput_result']['total']['DL']
+                            ['average_tput'],
+                            'NR DL Theoretical Throughput':
+                            result['nr_tput_result']['total']['DL']
+                            ['theoretical_tput'],
+                            'NR UL Min. Throughput':
+                            result['nr_tput_result']['total']['UL']
+                            ['min_tput'],
+                            'NR UL Max. Throughput':
+                            result['nr_tput_result']['total']['UL']
+                            ['max_tput'],
+                            'NR UL Avg. Throughput':
+                            result['nr_tput_result']['total']['UL']
+                            ['average_tput'],
+                            'NR UL Theoretical Throughput':
+                            result['nr_tput_result']['total']['UL']
+                            ['theoretical_tput'],
+                            'NR DL BLER (%)':
+                            result['nr_bler_result']['total']['DL']
+                            ['nack_ratio'] * 100,
+                            'NR UL BLER (%)':
+                            result['nr_bler_result']['total']['UL']
+                            ['nack_ratio'] * 100
+                        })
+                    if testcase_results['testcase_params'][
+                            'endc_combo_config']['lte_cell_count']:
+                        row_dict.update({
+                            'LTE DL Min. Throughput':
+                            result['lte_tput_result']['total']['DL']
+                            ['min_tput'],
+                            'LTE DL Max. Throughput':
+                            result['lte_tput_result']['total']['DL']
+                            ['max_tput'],
+                            'LTE DL Avg. Throughput':
+                            result['lte_tput_result']['total']['DL']
+                            ['average_tput'],
+                            'LTE DL Theoretical Throughput':
+                            result['lte_tput_result']['total']['DL']
+                            ['theoretical_tput'],
+                            'LTE UL Min. Throughput':
+                            result['lte_tput_result']['total']['UL']
+                            ['min_tput'],
+                            'LTE UL Max. Throughput':
+                            result['lte_tput_result']['total']['UL']
+                            ['max_tput'],
+                            'LTE UL Avg. Throughput':
+                            result['lte_tput_result']['total']['UL']
+                            ['average_tput'],
+                            'LTE UL Theoretical Throughput':
+                            result['lte_tput_result']['total']['UL']
+                            ['theoretical_tput'],
+                            'LTE DL BLER (%)':
+                            result['lte_bler_result']['total']['DL']
+                            ['nack_ratio'] * 100,
+                            'LTE UL BLER (%)':
+                            result['lte_bler_result']['total']['UL']
+                            ['nack_ratio'] * 100
+                        })
+                    writer.writerow(row_dict)
+
+    def get_per_cell_power_sweeps(self, testcase_params):
+        """Function to get per cell power sweep lists
+
+        Args:
+            testcase_params: dict containing all test case params
+        Returns:
+            cell_power_sweeps: list of cell power sweeps for each cell under test
+        """
+        cell_power_sweeps = []
+        for cell in testcase_params['endc_combo_config']['cell_list']:
+            if cell['cell_type'] == 'LTE':
+                sweep = [self.testclass_params['lte_cell_power']]
+            else:
+                sweep = [self.testclass_params['nr_cell_power']]
+            cell_power_sweeps.append(sweep)
+        return cell_power_sweeps
+
+
+class CellularLteFr1EndcPeakThroughputTest(CellularLtePlusFr1PeakThroughputTest
+                                           ):
+    """Class to test cellular LTE/FR1 ENDC combo list"""
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+        self.testclass_params = self.user_params['throughput_test_params']
+        self.tests = self.generate_test_cases([(27, 4), (4, 27)],
+                                              lte_dl_mcs_table='QAM256',
+                                              lte_ul_mcs_table='QAM256',
+                                              transform_precoding=0)
+
+    def generate_endc_combo_config(self, endc_combo_str):
+        """Function to generate ENDC combo config from combo string
+
+        Args:
+            endc_combo_str: ENDC combo descriptor (e.g. B48A[4];A[1]+N5A[2];A[1])
+        Returns:
+            endc_combo_config: dictionary with all ENDC combo settings
+        """
+        endc_combo_str = endc_combo_str.replace(' ', '')
+        endc_combo_list = endc_combo_str.split('+')
+        endc_combo_list = [combo.split(';') for combo in endc_combo_list]
+        endc_combo_config = collections.OrderedDict()
+        cell_config_list = list()
+        lte_cell_count = 0
+        nr_cell_count = 0
+        lte_scc_list = []
+        nr_dl_carriers = []
+        nr_ul_carriers = []
+        lte_carriers = []
+
+        for cell in endc_combo_list:
+            cell_config = {}
+            dl_config_str = cell[0]
+            dl_config_regex = re.compile(
+                r'(?P<cell_type>[B,N])(?P<band>[0-9]+)(?P<bandwidth_class>[A-Z])\[(?P<mimo_config>[0-9])\]'
+            )
+            dl_config_match = re.match(dl_config_regex, dl_config_str)
+            if dl_config_match.group('cell_type') == 'B':
+                cell_config['cell_type'] = 'LTE'
+                lte_cell_count = lte_cell_count + 1
+                cell_config['cell_number'] = lte_cell_count
+                if cell_config['cell_number'] == 1:
+                    cell_config['pcc'] = 1
+                    endc_combo_config['lte_pcc'] = cell_config['cell_number']
+                else:
+                    cell_config['pcc'] = 0
+                    lte_scc_list.append(cell_config['cell_number'])
+                cell_config['band'] = dl_config_match.group('band')
+                cell_config['duplex_mode'] = 'FDD' if int(
+                    cell_config['band']
+                ) in cputils.DUPLEX_MODE_TO_BAND_MAPPING['LTE'][
+                    'FDD'] else 'TDD'
+                cell_config['dl_mimo_config'] = 'D{nss}U{nss}'.format(
+                    nss=dl_config_match.group('mimo_config'))
+                if int(dl_config_match.group('mimo_config')) == 1:
+                    cell_config['transmission_mode'] = 'TM1'
+                elif int(dl_config_match.group('mimo_config')) == 2:
+                    cell_config['transmission_mode'] = 'TM2'
+                else:
+                    cell_config['transmission_mode'] = 'TM3'
+                lte_carriers.append(cell_config['cell_number'])
+            else:
+                cell_config['cell_type'] = 'NR5G'
+                nr_cell_count = nr_cell_count + 1
+                cell_config['cell_number'] = nr_cell_count
+                nr_dl_carriers.append(cell_config['cell_number'])
+                cell_config['band'] = 'N' + dl_config_match.group('band')
+                cell_config['duplex_mode'] = 'FDD' if cell_config[
+                    'band'] in cputils.DUPLEX_MODE_TO_BAND_MAPPING['NR5G'][
+                        'FDD'] else 'TDD'
+                cell_config['subcarrier_spacing'] = 'MU0' if cell_config[
+                    'duplex_mode'] == 'FDD' else 'MU1'
+                cell_config['dl_mimo_config'] = 'N{nss}X{nss}'.format(
+                    nss=dl_config_match.group('mimo_config'))
+
+            cell_config['dl_bandwidth_class'] = dl_config_match.group(
+                'bandwidth_class')
+            cell_config['dl_bandwidth'] = 'BW20'
+            cell_config['ul_enabled'] = len(cell) > 1
+            if cell_config['ul_enabled']:
+                ul_config_str = cell[1]
+                ul_config_regex = re.compile(
+                    r'(?P<bandwidth_class>[A-Z])\[(?P<mimo_config>[0-9])\]')
+                ul_config_match = re.match(ul_config_regex, ul_config_str)
+                cell_config['ul_bandwidth_class'] = ul_config_match.group(
+                    'bandwidth_class')
+                cell_config['ul_mimo_config'] = 'N{nss}X{nss}'.format(
+                    nss=ul_config_match.group('mimo_config'))
+                if cell_config['cell_type'] == 'NR5G':
+                    nr_ul_carriers.append(cell_config['cell_number'])
+            cell_config_list.append(cell_config)
+        endc_combo_config['lte_cell_count'] = lte_cell_count
+        endc_combo_config['nr_cell_count'] = nr_cell_count
+        endc_combo_config['nr_dl_carriers'] = nr_dl_carriers
+        endc_combo_config['nr_ul_carriers'] = nr_ul_carriers
+        endc_combo_config['cell_list'] = cell_config_list
+        endc_combo_config['lte_scc_list'] = lte_scc_list
+        endc_combo_config['lte_carriers'] = lte_carriers
+        return endc_combo_config
+
+    def generate_test_cases(self, mcs_pair_list, **kwargs):
+        test_cases = []
+
+        with open(self.testclass_params['endc_combo_file'],
+                  'r') as endc_combos:
+            for endc_combo_str in endc_combos:
+                if endc_combo_str[0] == '#':
+                    continue
+                endc_combo_config = self.generate_endc_combo_config(
+                    endc_combo_str)
+                special_chars = '+[];\n'
+                for char in special_chars:
+                    endc_combo_str = endc_combo_str.replace(char, '_')
+                endc_combo_str = endc_combo_str.replace('__', '_')
+                endc_combo_str = endc_combo_str.strip('_')
+                for mcs_pair in mcs_pair_list:
+                    test_name = 'test_lte_fr1_endc_{}_dl_mcs{}_ul_mcs{}'.format(
+                        endc_combo_str, mcs_pair[0], mcs_pair[1])
+                    test_params = collections.OrderedDict(
+                        endc_combo_config=endc_combo_config,
+                        nr_dl_mcs=mcs_pair[0],
+                        nr_ul_mcs=mcs_pair[1],
+                        lte_dl_mcs=mcs_pair[0],
+                        lte_ul_mcs=mcs_pair[1],
+                        **kwargs)
+                    setattr(self, test_name,
+                            partial(self._test_throughput_bler, test_params))
+                    test_cases.append(test_name)
+        return test_cases
+
+
+class CellularSingleCellThroughputTest(CellularLtePlusFr1PeakThroughputTest):
+    """Base Class to test single cell LTE or LTE/FR1"""
+
+    def generate_endc_combo_config(self, test_config):
+        """Function to generate ENDC combo config from CSV test config
+
+        Args:
+            test_config: dict containing ENDC combo config from CSV
+        Returns:
+            endc_combo_config: dictionary with all ENDC combo settings
+        """
+        endc_combo_config = collections.OrderedDict()
+        lte_cell_count = 0
+        nr_cell_count = 0
+        lte_scc_list = []
+        nr_dl_carriers = []
+        nr_ul_carriers = []
+        lte_carriers = []
+
+        cell_config_list = []
+        if test_config['lte_band']:
+            lte_cell = {
+                'cell_type':
+                'LTE',
+                'cell_number':
+                1,
+                'pcc':
+                1,
+                'band':
+                test_config['lte_band'],
+                'dl_bandwidth':
+                test_config['lte_bandwidth'],
+                'ul_enabled':
+                1,
+                'duplex_mode':
+                test_config['lte_duplex_mode'],
+                'dl_mimo_config':
+                'D{nss}U{nss}'.format(nss=test_config['lte_dl_mimo_config']),
+                'ul_mimo_config':
+                'D{nss}U{nss}'.format(nss=test_config['lte_ul_mimo_config'])
+            }
+            if int(test_config['lte_dl_mimo_config']) == 1:
+                lte_cell['transmission_mode'] = 'TM1'
+            elif int(test_config['lte_dl_mimo_config']) == 2:
+                lte_cell['transmission_mode'] = 'TM2'
+            else:
+                lte_cell['transmission_mode'] = 'TM3'
+            cell_config_list.append(lte_cell)
+            endc_combo_config['lte_pcc'] = 1
+            lte_cell_count = 1
+            lte_carriers = [1]
+
+        if test_config['nr_band']:
+            nr_cell = {
+                'cell_type':
+                'NR5G',
+                'cell_number':
+                1,
+                'band':
+                test_config['nr_band'],
+                'duplex_mode':
+                test_config['nr_duplex_mode'],
+                'dl_mimo_config':
+                'N{nss}X{nss}'.format(nss=test_config['nr_dl_mimo_config']),
+                'dl_bandwidth_class':
+                'A',
+                'dl_bandwidth':
+                test_config['nr_bandwidth'],
+                'ul_enabled':
+                1,
+                'ul_bandwidth_class':
+                'A',
+                'ul_mimo_config':
+                'N{nss}X{nss}'.format(nss=test_config['nr_ul_mimo_config']),
+                'subcarrier_spacing':
+                'MU0' if test_config['nr_scs'] == '15' else 'MU1'
+            }
+            cell_config_list.append(nr_cell)
+            nr_cell_count = 1
+            nr_dl_carriers = [1]
+            nr_ul_carriers = [1]
+
+        endc_combo_config['lte_cell_count'] = lte_cell_count
+        endc_combo_config['nr_cell_count'] = nr_cell_count
+        endc_combo_config['nr_dl_carriers'] = nr_dl_carriers
+        endc_combo_config['nr_ul_carriers'] = nr_ul_carriers
+        endc_combo_config['cell_list'] = cell_config_list
+        endc_combo_config['lte_scc_list'] = lte_scc_list
+        endc_combo_config['lte_carriers'] = lte_carriers
+        return endc_combo_config
+
+
+class CellularFr1SingleCellPeakThroughputTest(CellularSingleCellThroughputTest
+                                              ):
+    """Class to test single cell FR1 NSA mode"""
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+        self.testclass_params = self.user_params['throughput_test_params']
+        self.tests = self.generate_test_cases(
+            nr_mcs_pair_list=[(27, 4), (4, 27)],
+            nr_channel_list=['LOW', 'MID', 'HIGH'],
+            transform_precoding=0,
+            lte_dl_mcs=4,
+            lte_dl_mcs_table='QAM256',
+            lte_ul_mcs=4,
+            lte_ul_mcs_table='QAM64')
+
+    def generate_test_cases(self, nr_mcs_pair_list, nr_channel_list, **kwargs):
+
+        test_cases = []
+        with open(self.testclass_params['nr_single_cell_configs'],
+                  'r') as csvfile:
+            test_configs = csv.DictReader(csvfile)
+            for test_config, nr_channel, nr_mcs_pair in itertools.product(
+                    test_configs, nr_channel_list, nr_mcs_pair_list):
+                if int(test_config['skip_test']):
+                    continue
+                endc_combo_config = self.generate_endc_combo_config(
+                    test_config)
+                endc_combo_config['cell_list'][1]['channel'] = nr_channel
+                test_name = 'test_fr1_{}_{}_dl_mcs{}_ul_mcs{}'.format(
+                    test_config['nr_band'], nr_channel.lower(), nr_mcs_pair[0],
+                    nr_mcs_pair[1])
+                test_params = collections.OrderedDict(
+                    endc_combo_config=endc_combo_config,
+                    nr_dl_mcs=nr_mcs_pair[0],
+                    nr_ul_mcs=nr_mcs_pair[1],
+                    **kwargs)
+                setattr(self, test_name,
+                        partial(self._test_throughput_bler, test_params))
+                test_cases.append(test_name)
+        return test_cases
+
+
+class CellularLteSingleCellPeakThroughputTest(CellularSingleCellThroughputTest
+                                              ):
+    """Class to test single cell LTE"""
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+        self.testclass_params = self.user_params['throughput_test_params']
+        self.tests = self.generate_test_cases(lte_mcs_pair_list=[
+            (('QAM256', 27), ('QAM256', 4)), (('QAM256', 4), ('QAM256', 27))
+        ],
+                                              transform_precoding=0)
+
+    def generate_test_cases(self, lte_mcs_pair_list, **kwargs):
+        test_cases = []
+        with open(self.testclass_params['lte_single_cell_configs'],
+                  'r') as csvfile:
+            test_configs = csv.DictReader(csvfile)
+            for test_config, lte_mcs_pair in itertools.product(
+                    test_configs, lte_mcs_pair_list):
+                if int(test_config['skip_test']):
+                    continue
+                endc_combo_config = self.generate_endc_combo_config(
+                    test_config)
+                test_name = 'test_lte_B{}_dl_{}_mcs{}_ul_{}_mcs{}'.format(
+                    test_config['lte_band'], lte_mcs_pair[0][0],
+                    lte_mcs_pair[0][1], lte_mcs_pair[1][0], lte_mcs_pair[1][1])
+                test_params = collections.OrderedDict(
+                    endc_combo_config=endc_combo_config,
+                    lte_dl_mcs_table=lte_mcs_pair[0][0],
+                    lte_dl_mcs=lte_mcs_pair[0][1],
+                    lte_ul_mcs_table=lte_mcs_pair[1][0],
+                    lte_ul_mcs=lte_mcs_pair[1][1],
+                    **kwargs)
+                setattr(self, test_name,
+                        partial(self._test_throughput_bler, test_params))
+                test_cases.append(test_name)
+        return test_cases
diff --git a/acts_tests/tests/google/cellular/performance/CellularLteSensitivityTest.py b/acts_tests/tests/google/cellular/performance/CellularLteSensitivityTest.py
new file mode 100644
index 0000000..5e27bca
--- /dev/null
+++ b/acts_tests/tests/google/cellular/performance/CellularLteSensitivityTest.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import csv
+import itertools
+import numpy
+import json
+import re
+import os
+from acts import context
+from acts import base_test
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi.wifi_performance_test_utils.bokeh_figure import BokehFigure
+from CellularLtePlusFr1PeakThroughputTest import CellularLteSingleCellPeakThroughputTest
+
+from functools import partial
+
+
+class CellularLteSensitivityTest(CellularLteSingleCellPeakThroughputTest):
+    """Class to test single cell LTE sensitivity"""
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+        self.testclass_params = self.user_params['lte_sensitivity_test_params']
+        self.tests = self.generate_test_cases(dl_mcs_list=list(
+            numpy.arange(27, -1, -1)),
+                                              lte_dl_mcs_table='QAM256',
+                                              lte_ul_mcs_table='QAM256',
+                                              lte_ul_mcs=4,
+                                              transform_precoding=0)
+
+    def process_testclass_results(self):
+        # Plot individual test id results raw data and compile metrics
+        plots = collections.OrderedDict()
+        compiled_data = collections.OrderedDict()
+        for testcase_name, testcase_data in self.testclass_results.items():
+            cell_config = testcase_data['testcase_params'][
+                'endc_combo_config']['cell_list'][0]
+            test_id = tuple(('band', cell_config['band']))
+            if test_id not in plots:
+                # Initialize test id data when not present
+                compiled_data[test_id] = {
+                    'mcs': [],
+                    'average_throughput': [],
+                    'theoretical_throughput': [],
+                    'cell_power': [],
+                }
+                plots[test_id] = BokehFigure(
+                    title='Band {} ({}) - BLER Curves'.format(
+                        cell_config['band'],
+                        testcase_data['testcase_params']['lte_dl_mcs_table']),
+                    x_label='Cell Power (dBm)',
+                    primary_y_label='BLER (Mbps)')
+                test_id_rvr = test_id + tuple('RvR')
+                plots[test_id_rvr] = BokehFigure(
+                    title='Band {} ({}) - RvR'.format(
+                        cell_config['band'],
+                        testcase_data['testcase_params']['lte_dl_mcs_table']),
+                    x_label='Cell Power (dBm)',
+                    primary_y_label='PHY Rate (Mbps)')
+            # Compile test id data and metrics
+            compiled_data[test_id]['average_throughput'].append(
+                testcase_data['average_throughput_list'])
+            compiled_data[test_id]['cell_power'].append(
+                testcase_data['cell_power_list'])
+            compiled_data[test_id]['mcs'].append(
+                testcase_data['testcase_params']['lte_dl_mcs'])
+            # Add test id to plots
+            plots[test_id].add_line(
+                testcase_data['cell_power_list'],
+                testcase_data['bler_list'],
+                'MCS {}'.format(
+                    testcase_data['testcase_params']['lte_dl_mcs']),
+                width=1)
+            plots[test_id_rvr].add_line(
+                testcase_data['cell_power_list'],
+                testcase_data['average_throughput_list'],
+                'MCS {}'.format(
+                    testcase_data['testcase_params']['lte_dl_mcs']),
+                width=1,
+                style='dashed')
+
+        # Compute average RvRs and compute metrics over orientations
+        for test_id, test_data in compiled_data.items():
+            test_id_rvr = test_id + tuple('RvR')
+            cell_power_interp = sorted(set(sum(test_data['cell_power'], [])))
+            average_throughput_interp = []
+            for mcs, cell_power, throughput in zip(
+                    test_data['mcs'], test_data['cell_power'],
+                    test_data['average_throughput']):
+                throughput_interp = numpy.interp(cell_power_interp,
+                                                 cell_power[::-1],
+                                                 throughput[::-1])
+                average_throughput_interp.append(throughput_interp)
+            rvr = numpy.max(average_throughput_interp, 0)
+            plots[test_id_rvr].add_line(cell_power_interp, rvr,
+                                        'Rate vs. Range')
+
+        figure_list = []
+        for plot_id, plot in plots.items():
+            plot.generate_figure()
+            figure_list.append(plot)
+        output_file_path = os.path.join(self.log_path, 'results.html')
+        BokehFigure.save_figures(figure_list, output_file_path)
+
+    def process_testcase_results(self):
+        if self.current_test_name not in self.testclass_results:
+            return
+        testcase_data = self.testclass_results[self.current_test_name]
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            '{}.json'.format(self.current_test_name))
+        with open(results_file_path, 'w') as results_file:
+            json.dump(wputils.serialize_dict(testcase_data),
+                      results_file,
+                      indent=4)
+
+        bler_list = []
+        average_throughput_list = []
+        theoretical_throughput_list = []
+        cell_power_list = testcase_data['testcase_params']['cell_power_sweep'][
+            0]
+        for result in testcase_data['results']:
+            bler_list.append(
+                result['lte_bler_result']['total']['DL']['nack_ratio'])
+            average_throughput_list.append(
+                result['lte_tput_result']['total']['DL']['average_tput'])
+            theoretical_throughput_list.append(
+                result['lte_tput_result']['total']['DL']['theoretical_tput'])
+        padding_len = len(cell_power_list) - len(average_throughput_list)
+        average_throughput_list.extend([0] * padding_len)
+        theoretical_throughput_list.extend([0] * padding_len)
+
+        bler_above_threshold = [
+            bler > self.testclass_params['bler_threshold']
+            for bler in bler_list
+        ]
+        for idx in range(len(bler_above_threshold)):
+            if all(bler_above_threshold[idx:]):
+                sensitivity_idx = max(idx, 1) - 1
+                break
+        else:
+            sensitivity_idx = -1
+        sensitivity = cell_power_list[sensitivity_idx]
+        self.log.info('LTE Band {} Table {} MCS {} Sensitivity = {}dBm'.format(
+            testcase_data['testcase_params']['endc_combo_config']['cell_list']
+            [0]['band'], testcase_data['testcase_params']['lte_dl_mcs_table'],
+            testcase_data['testcase_params']['lte_dl_mcs'], sensitivity))
+
+        testcase_data['bler_list'] = bler_list
+        testcase_data['average_throughput_list'] = average_throughput_list
+        testcase_data[
+            'theoretical_throughput_list'] = theoretical_throughput_list
+        testcase_data['cell_power_list'] = cell_power_list
+        testcase_data['sensitivity'] = sensitivity
+
+    def get_per_cell_power_sweeps(self, testcase_params):
+        # get reference test
+        current_band = testcase_params['endc_combo_config']['cell_list'][0][
+            'band']
+        reference_test = None
+        reference_sensitivity = None
+        for testcase_name, testcase_data in self.testclass_results.items():
+            if testcase_data['testcase_params']['endc_combo_config'][
+                    'cell_list'][0]['band'] == current_band:
+                reference_test = testcase_name
+                reference_sensitivity = testcase_data['sensitivity']
+        if reference_test and reference_sensitivity and not self.retry_flag:
+            start_atten = reference_sensitivity + self.testclass_params[
+                'adjacent_mcs_gap']
+            self.log.info(
+                "Reference test {} found. Sensitivity {} dBm. Starting at {} dBm"
+                .format(reference_test, reference_sensitivity, start_atten))
+        else:
+            start_atten = self.testclass_params['lte_cell_power_start']
+            self.log.info(
+                "Reference test not found. Starting at {} dBm".format(
+                    start_atten))
+        # get current cell power start
+        cell_power_sweeps = [
+            list(
+                numpy.arange(start_atten,
+                             self.testclass_params['lte_cell_power_stop'],
+                             self.testclass_params['lte_cell_power_step']))
+        ]
+        return cell_power_sweeps
+
+    def generate_test_cases(self, dl_mcs_list, lte_dl_mcs_table,
+                            lte_ul_mcs_table, lte_ul_mcs, **kwargs):
+        test_cases = []
+        with open(self.testclass_params['lte_single_cell_configs'],
+                  'r') as csvfile:
+            test_configs = csv.DictReader(csvfile)
+            for test_config, lte_dl_mcs in itertools.product(
+                    test_configs, dl_mcs_list):
+                if int(test_config['skip_test']):
+                    continue
+                endc_combo_config = self.generate_endc_combo_config(
+                    test_config)
+                test_name = 'test_lte_B{}_dl_{}_mcs{}'.format(
+                    test_config['lte_band'], lte_dl_mcs_table, lte_dl_mcs)
+                test_params = collections.OrderedDict(
+                    endc_combo_config=endc_combo_config,
+                    lte_dl_mcs_table=lte_dl_mcs_table,
+                    lte_dl_mcs=lte_dl_mcs,
+                    lte_ul_mcs_table=lte_ul_mcs_table,
+                    lte_ul_mcs=lte_ul_mcs,
+                    **kwargs)
+                setattr(self, test_name,
+                        partial(self._test_throughput_bler, test_params))
+                test_cases.append(test_name)
+        return test_cases
diff --git a/acts_tests/tests/google/cellular/performance/CellularRxPowerTest.py b/acts_tests/tests/google/cellular/performance/CellularRxPowerTest.py
new file mode 100644
index 0000000..9615c91
--- /dev/null
+++ b/acts_tests/tests/google/cellular/performance/CellularRxPowerTest.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import collections
+import itertools
+import json
+import numpy
+import os
+import time
+from acts import asserts
+from acts import context
+from acts import base_test
+from acts import utils
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts_contrib.test_utils.cellular.keysight_5g_testapp import Keysight5GTestApp
+from acts_contrib.test_utils.cellular.performance import cellular_performance_test_utils as cputils
+from acts_contrib.test_utils.cellular.performance.shannon_log_parser import ShannonLogger
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts_contrib.test_utils.wifi.wifi_performance_test_utils.bokeh_figure import BokehFigure
+from functools import partial
+
+
+class CellularRxPowerTest(base_test.BaseTestClass):
+    """Class to test cellular throughput."""
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+        self.tests = self.generate_test_cases(['N257', 'N258', 'N260', 'N261'],
+                                              list(range(1, 9)))
+
+    def setup_class(self):
+        """Initializes common test hardware and parameters.
+
+        This function initializes hardwares and compiles parameters that are
+        common to all tests in this class.
+        """
+        self.dut = self.android_devices[-1]
+        self.testclass_params = self.user_params['rx_power_params']
+        self.keysight_test_app = Keysight5GTestApp(
+            self.user_params['Keysight5GTestApp'])
+        self.sdm_logger = ShannonLogger(self.dut)
+        self.testclass_results = collections.OrderedDict()
+        # Configure test retries
+        self.user_params['retry_tests'] = [self.__class__.__name__]
+
+        # Turn Airplane mode on
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+
+    def teardown_class(self):
+        self.log.info('Turning airplane mode on')
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+        self.keysight_test_app.set_cell_state('LTE', 1, 0)
+        self.keysight_test_app.destroy()
+
+    def setup_test(self):
+        cputils.start_pixel_logger(self.dut)
+
+    def on_retry(self):
+        """Function to control test logic on retried tests.
+
+        This function is automatically executed on tests that are being
+        retried. In this case the function resets wifi, toggles it off and on
+        and sets a retry_flag to enable further tweaking the test logic on
+        second attempts.
+        """
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+        if self.keysight_test_app.get_cell_state('LTE', 'CELL1'):
+            self.log.info('Turning LTE off.')
+            self.keysight_test_app.set_cell_state('LTE', 'CELL1', 0)
+
+    def teardown_test(self):
+        self.log.info('Turning airplane mode on')
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+        log_path = os.path.join(
+            context.get_current_context().get_full_output_path(), 'pixel_logs')
+        os.makedirs(log_path, exist_ok=True)
+        self.log.info(self.current_test_info)
+        self.testclass_results.setdefault(self.current_test_name,
+                                          collections.OrderedDict())
+        self.testclass_results[self.current_test_name].setdefault(
+            'log_path', [])
+        self.testclass_results[self.current_test_name]['log_path'].append(
+            cputils.stop_pixel_logger(self.dut, log_path))
+        self.process_test_results()
+
+    def process_test_results(self):
+        test_result = self.testclass_results[self.current_test_name]
+
+        # Save output as text file
+        results_file_path = os.path.join(
+            self.log_path, '{}.json'.format(self.current_test_name))
+        with open(results_file_path, 'w') as results_file:
+            json.dump(wputils.serialize_dict(test_result),
+                      results_file,
+                      indent=4)
+        # Plot and save
+        if test_result['log_path']:
+            log_data = self.sdm_logger.process_log(test_result['log_path'][-1])
+        else:
+            return
+        figure = BokehFigure(title=self.current_test_name,
+                             x_label='Cell Power Setting (dBm)',
+                             primary_y_label='Time')
+        figure.add_line(log_data.lte.rsrp_time, log_data.lte.rsrp_rx0,
+                        'LTE RSRP (Rx0)')
+        figure.add_line(log_data.lte.rsrp_time, log_data.lte.rsrp_rx1,
+                        'LTE RSRP (Rx1)')
+        figure.add_line(log_data.lte.rsrp2_time, log_data.lte.rsrp2_rx0,
+                        'LTE RSRP2 (Rx0)')
+        figure.add_line(log_data.lte.rsrp2_time, log_data.lte.rsrp2_rx1,
+                        'LTE RSRP2 (Rx0)')
+        figure.add_line(log_data.nr.rsrp_time, log_data.nr.rsrp_rx0,
+                        'NR RSRP (Rx0)')
+        figure.add_line(log_data.nr.rsrp_time, log_data.nr.rsrp_rx1,
+                        'NR RSRP (Rx1)')
+        figure.add_line(log_data.nr.rsrp2_time, log_data.nr.rsrp2_rx0,
+                        'NR RSRP2 (Rx0)')
+        figure.add_line(log_data.nr.rsrp2_time, log_data.nr.rsrp2_rx1,
+                        'NR RSRP2 (Rx0)')
+        figure.add_line(log_data.fr2.rsrp0_time, log_data.fr2.rsrp0,
+                        'NR RSRP (Rx0)')
+        figure.add_line(log_data.fr2.rsrp1_time, log_data.fr2.rsrp1,
+                        'NR RSRP2 (Rx1)')
+        output_file_path = os.path.join(
+            self.log_path, '{}.html'.format(self.current_test_name))
+        figure.generate_figure(output_file_path)
+
+    def _test_nr_rsrp(self, testcase_params):
+        """Test function to run cellular RSRP tests.
+
+        The function runs a sweep of cell powers while collecting pixel logs
+        for later postprocessing and RSRP analysis.
+
+        Args:
+            testcase_params: dict containing test-specific parameters
+        """
+
+        result = collections.OrderedDict()
+        testcase_params['power_range_vector'] = list(
+            numpy.arange(self.testclass_params['cell_power_start'],
+                         self.testclass_params['cell_power_stop'],
+                         self.testclass_params['cell_power_step']))
+
+        if not self.keysight_test_app.get_cell_state('LTE', 'CELL1'):
+            self.log.info('Turning LTE on.')
+            self.keysight_test_app.set_cell_state('LTE', 'CELL1', 1)
+        self.log.info('Turning off airplane mode')
+        asserts.assert_true(utils.force_airplane_mode(self.dut, False),
+                            'Can not turn on airplane mode.')
+
+        for cell in testcase_params['dl_cell_list']:
+            self.keysight_test_app.set_cell_band('NR5G', cell,
+                                                 testcase_params['band'])
+        # Consider configuring schedule quick config
+        self.keysight_test_app.set_nr_cell_schedule_scenario(
+            testcase_params['dl_cell_list'][0], 'BASIC')
+        self.keysight_test_app.set_dl_carriers(testcase_params['dl_cell_list'])
+        self.keysight_test_app.set_ul_carriers(
+            testcase_params['dl_cell_list'][0])
+        self.log.info('Waiting for LTE and applying aggregation')
+        if not self.keysight_test_app.wait_for_cell_status(
+                'LTE', 'CELL1', 'CONN', 60):
+            asserts.fail('DUT did not connect to LTE.')
+        self.keysight_test_app.apply_carrier_agg()
+        self.log.info('Waiting for 5G connection')
+        connected = self.keysight_test_app.wait_for_cell_status(
+            'NR5G', testcase_params['dl_cell_list'][-1], ['ACT', 'CONN'], 60)
+        if not connected:
+            asserts.fail('DUT did not connect to NR.')
+        for cell_power in testcase_params['power_range_vector']:
+            self.log.info('Setting power to {} dBm'.format(cell_power))
+            for cell in testcase_params['dl_cell_list']:
+                self.keysight_test_app.set_cell_dl_power(
+                    'NR5G', cell, cell_power, True)
+            #measure RSRP
+            self.keysight_test_app.start_nr_rsrp_measurement(
+                testcase_params['dl_cell_list'],
+                self.testclass_params['rsrp_measurement_duration'])
+            time.sleep(self.testclass_params['rsrp_measurement_duration'] *
+                       1.5 / 1000)
+            self.keysight_test_app.get_nr_rsrp_measurement_state(
+                testcase_params['dl_cell_list'])
+            self.keysight_test_app.get_nr_rsrp_measurement_results(
+                testcase_params['dl_cell_list'])
+
+        for cell in testcase_params['dl_cell_list'][::-1]:
+            self.keysight_test_app.set_cell_state('NR5G', cell, 0)
+        asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+                            'Can not turn on airplane mode.')
+        # Save results
+        result['testcase_params'] = testcase_params
+        self.testclass_results[self.current_test_name] = result
+        results_file_path = os.path.join(
+            context.get_current_context().get_full_output_path(),
+            '{}.json'.format(self.current_test_name))
+        with open(results_file_path, 'w') as results_file:
+            json.dump(wputils.serialize_dict(result), results_file, indent=4)
+
+    def generate_test_cases(self, bands, num_cells_list):
+        """Function that auto-generates test cases for a test class."""
+        test_cases = []
+
+        for band, num_cells in itertools.product(bands, num_cells_list):
+            test_name = 'test_nr_rsrp_{}_{}CC'.format(band, num_cells)
+            test_params = collections.OrderedDict(band=band,
+                                                  num_cells=num_cells,
+                                                  dl_cell_list=list(
+                                                      range(1, num_cells + 1)))
+            setattr(self, test_name, partial(self._test_nr_rsrp, test_params))
+            test_cases.append(test_name)
+        return test_cases
diff --git a/acts_tests/tests/google/cellular/performance/__init__.py b/acts_tests/tests/google/cellular/performance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts_tests/tests/google/cellular/performance/__init__.py
diff --git a/acts_tests/tests/google/fuchsia/backlight/BacklightTest.py b/acts_tests/tests/google/fuchsia/backlight/BacklightTest.py
deleted file mode 100644
index 9cc6c0c..0000000
--- a/acts_tests/tests/google/fuchsia/backlight/BacklightTest.py
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-import os
-import time
-import uuid
-
-from acts import asserts, signals
-from acts.base_test import BaseTestClass
-from acts.libs.proc.job import Error
-from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
-
-BRIGHTNESS_CHANGE_SLEEP_TIME_SECONDS = 2
-
-ONE_QUARTER_BRIGHTNESS = 0.25
-HALF_BRIGHTNESS = 0.5
-THREE_QUARTERS_BRIGHTNESS = 0.75
-FULL_BRIGHTNESS = 1.0
-
-
-def float_near(a, b):
-    return abs(a - b) < 0.001
-
-
-class BacklightTest(BaseTestClass):
-    def setup_class(self):
-        super().setup_class()
-        self.fd = self.fuchsia_devices[0]
-
-    def setup_test(self):
-        # Save the state and scale so that they can be restored after the test. Set a known initial
-        # brightness so that the test effects are consistent.
-        self.initial_state = self.fd.backlight_lib.getStateNormalized(
-        )['result']
-        self.initial_scale = self.fd.backlight_lib.getNormalizedBrightnessScale(
-        )['result']
-
-        self.fd.backlight_lib.setStateNormalized(True, HALF_BRIGHTNESS)
-
-    def teardown_test(self):
-        self.fd.backlight_lib.setNormalizedBrightnessScale(self.initial_scale)
-
-        backlight_on = self.initial_state['backlight_on']
-        brightness = self.initial_state['brightness']
-        self.fd.backlight_lib.setStateNormalized(backlight_on,
-                                                 brightness * HALF_BRIGHTNESS)
-        self.fd.backlight_lib.setStateNormalized(backlight_on, brightness)
-
-    def test_brightness(self):
-        """Test SetStateNormalized and GetStateNormalized FIDL calls.
-
-        The display brightness should decrease, increase, turn off, then turn back on again. This
-        test will only run if the device config has a 'backlight_tests' entry.
-        """
-        asserts.skip_if('backlight_tests' not in self.user_params,
-                        'backlight_tests not specified in the config')
-
-        # Pause to make backlight brightness changes clearly visible.
-        time.sleep(BRIGHTNESS_CHANGE_SLEEP_TIME_SECONDS)
-
-        result = self.fd.backlight_lib.setStateNormalized(
-            True, ONE_QUARTER_BRIGHTNESS)
-        asserts.assert_true(result['error'] is None,
-                            'SetStateNormalized failed')
-
-        result = self.fd.backlight_lib.getStateNormalized()
-        asserts.assert_true(result['error'] is None,
-                            'GetStateNormalized failed')
-
-        asserts.assert_true(result['result']['backlight_on'],
-                            'Got unexpected device state')
-        asserts.assert_true(
-            float_near(result['result']['brightness'], ONE_QUARTER_BRIGHTNESS),
-            'Got unexpected brightness value')
-
-        time.sleep(BRIGHTNESS_CHANGE_SLEEP_TIME_SECONDS)
-
-        # Brightness should increase
-        result = self.fd.backlight_lib.setStateNormalized(
-            True, THREE_QUARTERS_BRIGHTNESS)
-        asserts.assert_true(result['error'] is None,
-                            'SetStateNormalized failed')
-
-        result = self.fd.backlight_lib.getStateNormalized()
-        asserts.assert_true(result['error'] is None,
-                            'GetStateNormalized failed')
-
-        asserts.assert_true(result['result']['backlight_on'],
-                            'Got unexpected device state')
-        asserts.assert_true(
-            float_near(result['result']['brightness'],
-                       THREE_QUARTERS_BRIGHTNESS),
-            'Got unexpected brightness value')
-
-        time.sleep(BRIGHTNESS_CHANGE_SLEEP_TIME_SECONDS)
-
-        # Backlight should turn off
-        result = self.fd.backlight_lib.setStateNormalized(
-            False, FULL_BRIGHTNESS)
-        asserts.assert_true(result['error'] is None,
-                            'SetStateNormalized failed')
-
-        result = self.fd.backlight_lib.getStateNormalized()
-        asserts.assert_true(result['error'] is None,
-                            'GetStateNormalized failed')
-
-        asserts.assert_false(result['result']['backlight_on'],
-                             'Got unexpected device state')
-        asserts.assert_true(
-            float_near(result['result']['brightness'], FULL_BRIGHTNESS),
-            'Got unexpected brightness value')
-
-    def test_brightness_scale(self):
-        """Test SetNormalizedBrightnessScale and SetNormalizedBrightnessScale FIDL calls.
-
-        The display brightness should decrease then increase. This test will only run if the device
-        config has a 'backlight_tests' entry.
-        """
-        asserts.skip_if('backlight_tests' not in self.user_params,
-                        'backlight_tests not specified in the config')
-
-        time.sleep(BRIGHTNESS_CHANGE_SLEEP_TIME_SECONDS)
-
-        # Brightness should decrease
-        result = self.fd.backlight_lib.setNormalizedBrightnessScale(
-            self.initial_scale * ONE_QUARTER_BRIGHTNESS)
-        asserts.assert_true(result['error'] is None,
-                            'SetNormalizedBrightnessScale failed')
-
-        result = self.fd.backlight_lib.getNormalizedBrightnessScale()
-        asserts.assert_true(result['error'] is None,
-                            'GetNormalizedBrightnessScale failed')
-
-        asserts.assert_true(
-            float_near(result['result'],
-                       self.initial_scale * ONE_QUARTER_BRIGHTNESS),
-            'Got unexpected brightness scale value')
-
-        # Toggle the brightness to force an update. This works around a hardware limitation.
-        self.fd.backlight_lib.setStateNormalized(True, ONE_QUARTER_BRIGHTNESS)
-        self.fd.backlight_lib.setStateNormalized(True, HALF_BRIGHTNESS)
-
-        time.sleep(BRIGHTNESS_CHANGE_SLEEP_TIME_SECONDS)
-
-        # Brightness should increase
-        result = self.fd.backlight_lib.setNormalizedBrightnessScale(
-            self.initial_scale)
-        asserts.assert_true(result['error'] is None,
-                            'SetNormalizedBrightnessScale failed')
-
-        result = self.fd.backlight_lib.getNormalizedBrightnessScale()
-        asserts.assert_true(result['error'] is None,
-                            'GetNormalizedBrightnessScale failed')
-
-        asserts.assert_true(float_near(result['result'], self.initial_scale),
-                            'Got unexpected brightness scale value')
-
-        self.fd.backlight_lib.setStateNormalized(True, ONE_QUARTER_BRIGHTNESS)
-        self.fd.backlight_lib.setStateNormalized(True, HALF_BRIGHTNESS)
diff --git a/acts_tests/tests/google/fuchsia/bt/BleFuchsiaAndroidTest.py b/acts_tests/tests/google/fuchsia/bt/BleFuchsiaAndroidTest.py
index 636db87..33d297a 100644
--- a/acts_tests/tests/google/fuchsia/bt/BleFuchsiaAndroidTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/BleFuchsiaAndroidTest.py
@@ -18,8 +18,6 @@
 """
 
 import pprint
-import random
-import time
 
 from acts.controllers import android_device
 from acts_contrib.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
@@ -76,9 +74,6 @@
                 self.fd, self.log, sample_android_name, self.default_timeout):
             res = False
 
-        #Print clients to validate results are saved
-        self.fd.print_clients()
-
         #Stop android advertising
         self.ad.droid.bleStopBleAdvertising(adv_callback)
 
@@ -104,17 +99,17 @@
         name, did, connectable = scan_result["name"], scan_result[
             "id"], scan_result["connectable"]
 
-        connect = self.fd.gattc_lib.bleConnectToPeripheral(did)
+        connect = self.fd.sl4f.gattc_lib.bleConnectToPeripheral(did)
         self.log.info("Connecting returned status: {}".format(connect))
 
-        services = self.fd.gattc_lib.listServices(did)
+        services = self.fd.sl4f.gattc_lib.listServices(did)
         self.log.info("Listing services returned: {}".format(services))
 
-        dconnect = self.fd.gattc_lib.bleDisconnectPeripheral(did)
+        dconnect = self.fd.sl4f.gattc_lib.bleDisconnectPeripheral(did)
         self.log.info("Disconnect status: {}".format(dconnect))
 
         #Print clients to validate results are saved
-        self.fd.print_clients()
+        self.fd.sl4f.print_clients()
 
         #Stop android advertising + cleanup sl4f
         self.ad.droid.bleStopBleAdvertising(adv_callback)
@@ -141,8 +136,8 @@
         interval = 1000
 
         #Start advertising
-        self.fd.ble_lib.bleStartBleAdvertising(adv_data, scan_response,
-                                               interval, connectable)
+        self.fd.sl4f.ble_lib.bleStartBleAdvertising(adv_data, scan_response,
+                                                    interval, connectable)
 
         # Initialize scan on android device which scan settings + callback
         filter_list = self.ad.droid.bleGenFilterList()
@@ -163,7 +158,7 @@
             self.log.error("Didn't find any scan results.")
             return False
         finally:
-            self.fd.ble_lib.bleStopBleAdvertising()
+            self.fd.sl4f.ble_lib.bleStopBleAdvertising()
             self.ad.droid.bleStopBleScan(scan_callback)
         # TODO(): Validate result
         return True
diff --git a/acts_tests/tests/google/fuchsia/bt/BleFuchsiaTest.py b/acts_tests/tests/google/fuchsia/bt/BleFuchsiaTest.py
index a74db66..cf663d2 100644
--- a/acts_tests/tests/google/fuchsia/bt/BleFuchsiaTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/BleFuchsiaTest.py
@@ -16,9 +16,7 @@
 """This scrip tests various BLE apis for Fuchsia devices.
 """
 
-import pprint
 import random
-import time
 
 from acts.base_test import BaseTestClass
 from acts_contrib.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
@@ -40,15 +38,14 @@
         self.fuchsia_scan = self.fuchsia_devices[1]
 
     def test_fuchsia_publish_service(self):
-        service_id = 0
         service_primary = True
         # Random uuid
         service_type = "0000180f-0000-1000-8000-00805fffffff"
 
         # Generate a random key for sl4f storage of proxy key
         service_proxy_key = "SProxy" + str(random.randint(0, 1000000))
-        res = self.fuchsia_adv.ble_lib.blePublishService(
-            service_id, service_primary, service_type, service_proxy_key)
+        res = self.fuchsia_adv.sl4f.ble_lib.blePublishService(
+            service_primary, service_type, service_proxy_key)
         self.log.info("Publish result: {}".format(res))
 
         return True
@@ -71,7 +68,7 @@
         res = True
 
         # Start advertising
-        self.fuchsia_adv.ble_lib.bleStartBleAdvertising(
+        self.fuchsia_adv.sl4f.ble_lib.bleStartBleAdvertising(
             adv_data, scan_response, interval, connectable)
         self.log.info("Fuchsia advertising name: {}".format(fuchsia_name))
 
@@ -83,21 +80,20 @@
             res = False
 
         # Stop advertising
-        self.fuchsia_adv.ble_lib.bleStopBleAdvertising()
+        self.fuchsia_adv.sl4f.ble_lib.bleStopBleAdvertising()
 
         return res
 
     def test_fuchsia_gatt_fuchsia_periph(self):
-        # Create random service with id, primary, and uuid
-        service_id = 3
+        # Create random service with primary, and uuid
         service_primary = True
         # Random uuid
         service_type = "0000180f-0000-1000-8000-00805fffffff"
 
         # Generate a random key for sl4f storage of proxy key
         service_proxy_key = "SProxy" + str(random.randint(0, 1000000))
-        res = self.fuchsia_adv.ble_lib.blePublishService(
-            service_id, service_primary, service_type, service_proxy_key)
+        res = self.fuchsia_adv.sl4f.ble_lib.blePublishService(
+            service_primary, service_type, service_proxy_key)
         self.log.info("Publish result: {}".format(res))
 
         # Initialize advertising on fuchsia dveice with name and interval
@@ -116,7 +112,7 @@
         interval = 1000
 
         # Start advertising
-        self.fuchsia_adv.ble_lib.bleStartBleAdvertising(
+        self.fuchsia_adv.sl4f.ble_lib.bleStartBleAdvertising(
             adv_data, scan_response, interval, connectable)
         self.log.info("Fuchsia advertising name: {}".format(fuchsia_name))
 
@@ -125,22 +121,23 @@
                                                  fuchsia_name,
                                                  self.default_timeout)
         if not scan_result:
-            self.fuchsia_adv.ble_lib.bleStopBleAdvertising()
+            self.fuchsia_adv.sl4f.ble_lib.bleStopBleAdvertising()
             return False
 
         name, did, connectable = scan_result["name"], scan_result[
             "id"], scan_result["connectable"]
 
-        connect = self.fuchsia_scan.gattc_lib.bleConnectToPeripheral(did)
+        connect = self.fuchsia_scan.sl4f.gattc_lib.bleConnectToPeripheral(did)
         self.log.info("Connecting returned status: {}".format(connect))
 
-        services = self.fuchsia_scan.gattc_lib.listServices(did)
+        services = self.fuchsia_scan.sl4f.gattc_lib.listServices(did)
         self.log.info("Listing services returned: {}".format(services))
 
-        dconnect = self.fuchsia_scan.gattc_lib.bleDisconnectPeripheral(did)
+        dconnect = self.fuchsia_scan.sl4f.gattc_lib.bleDisconnectPeripheral(
+            did)
         self.log.info("Disconnect status: {}".format(dconnect))
 
         # Stop fuchsia advertising
-        self.fuchsia_adv.ble_lib.bleStopBleAdvertising()
+        self.fuchsia_adv.sl4f.ble_lib.bleStopBleAdvertising()
 
         return True
diff --git a/acts_tests/tests/google/fuchsia/bt/BluetoothCmdLineTest.py b/acts_tests/tests/google/fuchsia/bt/BluetoothCmdLineTest.py
index 76a2cf1..6859e38 100644
--- a/acts_tests/tests/google/fuchsia/bt/BluetoothCmdLineTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/BluetoothCmdLineTest.py
@@ -21,7 +21,6 @@
 """
 from acts.base_test import BaseTestClass
 from command_input import CommandInput
-from queue import Empty
 
 
 class BluetoothCmdLineTest(BaseTestClass):
@@ -33,8 +32,8 @@
         if dut:
             if dut == 'fuchsia_devices':
                 self.dut = self.fuchsia_devices[0]
-                self.dut.bts_lib.initBluetoothSys()
-                self.dut.sdp_lib.init()
+                self.dut.sl4f.bts_lib.initBluetoothSys()
+                self.dut.sl4f.sdp_lib.init()
             elif dut == 'android_devices':
                 self.dut = self.android_devices[0]
             else:
diff --git a/acts_tests/tests/google/fuchsia/bt/FuchsiaBtMacAddressTest.py b/acts_tests/tests/google/fuchsia/bt/FuchsiaBtMacAddressTest.py
index 2ce6b54..4adbce0 100644
--- a/acts_tests/tests/google/fuchsia/bt/FuchsiaBtMacAddressTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/FuchsiaBtMacAddressTest.py
@@ -21,8 +21,6 @@
 This test requires at least two fuchsia devices.
 """
 
-import time
-
 from acts import signals
 from acts.base_test import BaseTestClass
 from acts.test_decorators import test_tracker_info
@@ -38,7 +36,7 @@
         if len(self.fuchsia_devices) < 2:
             raise signals.TestAbortAll("Need at least two Fuchsia devices")
         for device in self.fuchsia_devices:
-            device.bts_lib.initBluetoothSys()
+            device.sl4f.bts_lib.initBluetoothSys()
 
     # TODO: add @test_tracker_info(uuid='')
     def test_verify_different_mac_addresses(self):
@@ -60,7 +58,7 @@
         mac_addr_list = []
         for device in self.fuchsia_devices:
             mac_addr_list.append(
-                device.bts_lib.getActiveAdapterAddress().get("result"))
+                device.sl4f.bts_lib.getActiveAdapterAddress().get("result"))
         if len(mac_addr_list) != len(set(mac_addr_list)):
             raise signals.TestFailure(
                 "Found duplicate mac addresses {}.".format(mac_addr_list))
diff --git a/acts_tests/tests/google/fuchsia/bt/FuchsiaBtScanTest.py b/acts_tests/tests/google/fuchsia/bt/FuchsiaBtScanTest.py
index 074d3e5..346155a 100644
--- a/acts_tests/tests/google/fuchsia/bt/FuchsiaBtScanTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/FuchsiaBtScanTest.py
@@ -37,8 +37,8 @@
         self.pri_dut = self.fuchsia_devices[0]
         self.sec_dut = self.fuchsia_devices[1]
 
-        self.pri_dut.bts_lib.initBluetoothSys()
-        self.sec_dut.bts_lib.initBluetoothSys()
+        self.pri_dut.sl4f.bts_lib.initBluetoothSys()
+        self.sec_dut.sl4f.bts_lib.initBluetoothSys()
 
     # TODO: add @test_tracker_info(uuid='')
     def test_scan_with_peer_set_non_discoverable(self):
@@ -62,13 +62,13 @@
         Priority: 1
         """
         local_name = generate_id_by_size(10)
-        self.sec_dut.bts_lib.setName(local_name)
-        self.sec_dut.bts_lib.setDiscoverable(False)
+        self.sec_dut.sl4f.bts_lib.setName(local_name)
+        self.sec_dut.sl4f.bts_lib.setDiscoverable(False)
 
-        self.pri_dut.bts_lib.requestDiscovery(True)
+        self.pri_dut.sl4f.bts_lib.requestDiscovery(True)
         time.sleep(self.scan_timeout_seconds)
-        self.pri_dut.bts_lib.requestDiscovery(False)
-        discovered_devices = self.pri_dut.bts_lib.getKnownRemoteDevices()
+        self.pri_dut.sl4f.bts_lib.requestDiscovery(False)
+        discovered_devices = self.pri_dut.sl4f.bts_lib.getKnownRemoteDevices()
         for device in discovered_devices.get("result").values():
             discoverd_name = device.get("name")
             if discoverd_name is not None and discoverd_name is local_name:
@@ -99,20 +99,21 @@
         """
         local_name = generate_id_by_size(10)
         self.log.info("Setting local peer name to: {}".format(local_name))
-        self.sec_dut.bts_lib.setName(local_name)
-        self.sec_dut.bts_lib.setDiscoverable(True)
+        self.sec_dut.sl4f.bts_lib.setName(local_name)
+        self.sec_dut.sl4f.bts_lib.setDiscoverable(True)
 
-        self.pri_dut.bts_lib.requestDiscovery(True)
+        self.pri_dut.sl4f.bts_lib.requestDiscovery(True)
         end_time = time.time() + self.scan_timeout_seconds
         poll_timeout = 10
         while time.time() < end_time:
-            discovered_devices = self.pri_dut.bts_lib.getKnownRemoteDevices()
+            discovered_devices = self.pri_dut.sl4f.bts_lib.getKnownRemoteDevices(
+            )
             for device in discovered_devices.get("result").values():
                 self.log.info(device)
                 discoverd_name = device.get("name")
                 if discoverd_name is not None and discoverd_name in local_name:
-                    self.pri_dut.bts_lib.requestDiscovery(False)
+                    self.pri_dut.sl4f.bts_lib.requestDiscovery(False)
                     raise signals.TestPass("Successfully found peer device.")
             time.sleep(poll_timeout)
-        self.pri_dut.bts_lib.requestDiscovery(False)
+        self.pri_dut.sl4f.bts_lib.requestDiscovery(False)
         raise signals.TestFailure("Unable to find peer device.")
diff --git a/acts_tests/tests/google/fuchsia/bt/GapSecSemTest.py b/acts_tests/tests/google/fuchsia/bt/GapSecSemTest.py
index 97be69d..f4f51c1 100644
--- a/acts_tests/tests/google/fuchsia/bt/GapSecSemTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/GapSecSemTest.py
@@ -30,7 +30,6 @@
 from acts_contrib.test_utils.fuchsia.bt_test_utils import unbond_all_known_devices
 from contextlib import suppress
 import inspect
-import time
 
 
 class GapSecSemTest(BaseTestClass):
@@ -46,7 +45,7 @@
         # for the interim set this manually in the build.
         self.sec_dut = self.fuchsia_devices[1]
         for fd in self.fuchsia_devices:
-            fd.bts_lib.initBluetoothSys()
+            fd.sl4f.bts_lib.initBluetoothSys()
         # Optional user param for collecting enough information for
         # certification on pass results.
         self.collect_detailed_pass_logs = self.user_params.get(
@@ -60,14 +59,14 @@
         # Stop scanning and advertising on all devices at the end of a test.
         with suppress(Exception):
             for fd in self.fuchsia_devices:
-                fd.ble_lib.bleStopBleAdvertising()
+                fd.sl4f.ble_lib.bleStopBleAdvertising()
                 fd.bleStopBleScan()
         for fd in self.fuchsia_devices:
             unbond_all_known_devices(fd, self.log)
 
     def teardown_class(self):
         for fd in self.fuchsia_devices:
-            fd.bts_lib.requestDiscovery(False)
+            fd.sl4f.bts_lib.requestDiscovery(False)
 
     def on_pass(self, test_name, begin_time):
         if self.collect_detailed_pass_logs == True:
@@ -98,11 +97,10 @@
         scan_response = None
         connectable = True
 
-        peripheral.ble_lib.bleStartBleAdvertising(adv_data, scan_response,
-                                                  self.ble_advertise_interval,
-                                                  connectable)
+        peripheral.sl4f.ble_lib.bleStartBleAdvertising(
+            adv_data, scan_response, self.ble_advertise_interval, connectable)
         scan_filter = {"name_substring": adv_name}
-        central.gattc_lib.bleStartBleScan(scan_filter)
+        central.sl4f.gattc_lib.bleStartBleScan(scan_filter)
         device = le_scan_for_device_by_name(central,
                                             self.log,
                                             adv_name,
@@ -111,7 +109,8 @@
                                             self_manage_scan=False)
         if device is None:
             raise signals.TestFailure("Scanner unable to find advertisement.")
-        connect_result = central.gattc_lib.bleConnectToPeripheral(device["id"])
+        connect_result = central.sl4f.gattc_lib.bleConnectToPeripheral(
+            device["id"])
         if connect_result.get("error") is not None:
             raise signals.TestFailure(
                 self.gatt_connect_err_message.format(
@@ -147,9 +146,9 @@
         input_capabilities = "NONE"
         output_capabilities = "NONE"
 
-        central.bts_lib.acceptPairing("KEYBOARD", "DISPLAY")
+        central.sl4f.bts_lib.acceptPairing("KEYBOARD", "DISPLAY")
 
-        peripheral.bts_lib.acceptPairing("KEYBOARD", "DISPLAY")
+        peripheral.sl4f.bts_lib.acceptPairing("KEYBOARD", "DISPLAY")
 
         device = self._orchestrate_gatt_connection(central, peripheral)
         # TODO: fxb/71289 Change once all peer IDs are ints and not strings
@@ -157,28 +156,29 @@
         bondable = True
         transport = 2  #LE
         if is_central_pairing_initiator:
-            pair_result = central.bts_lib.pair(identifier, security_level,
-                                               bondable, transport)
+            pair_result = central.sl4f.bts_lib.pair(identifier, security_level,
+                                                    bondable, transport)
         if not is_central_pairing_initiator:
-            device_list = peripheral.bts_lib.getKnownRemoteDevices()['result']
+            device_list = peripheral.sl4f.bts_lib.getKnownRemoteDevices(
+            )['result']
             print(device_list)
             for id_dict in device_list:
                 d = device_list[id_dict]
                 name = None
                 if d['connected'] is True:
                     did = d['id']
-            pair_result = peripheral.bts_lib.pair(did, security_level,
-                                                  bondable, transport)
+            pair_result = peripheral.sl4f.bts_lib.pair(did, security_level,
+                                                       bondable, transport)
 
         pins_transferred = False
-        pairing_pin = central.bts_lib.getPairingPin()['result']
+        pairing_pin = central.sl4f.bts_lib.getPairingPin()['result']
         if pairing_pin != "0" and pairing_pin is not None:
-            peripheral.bts_lib.inputPairingPin(pairing_pin)
+            peripheral.sl4f.bts_lib.inputPairingPin(pairing_pin)
             pins_transferred = True
         if not pins_transferred:
-            pairing_pin = peripheral.bts_lib.getPairingPin()['result']
+            pairing_pin = peripheral.sl4f.bts_lib.getPairingPin()['result']
             if pairing_pin != "0":
-                central.bts_lib.inputPairingPin(pairing_pin)
+                central.sl4f.bts_lib.inputPairingPin(pairing_pin)
 
         if self.collect_detailed_pass_logs == True:
             save_path = f"{central.log_path}/{test_name}_stash_secure.store"
@@ -188,7 +188,7 @@
             self.log.info(
                 f"Known Link Keys: {get_link_keys(peripheral, save_path)}")
 
-        disconnect_result = central.gattc_lib.bleDisconnectPeripheral(
+        disconnect_result = central.sl4f.gattc_lib.bleDisconnectPeripheral(
             device["id"])
         if disconnect_result.get("error") is not None:
             raise signals.TestFailure(
@@ -196,7 +196,7 @@
                     disconnect_result.get("error")))
         self.log.info("Disconnection Successful...")
 
-        central.bts_lib.forgetDevice(identifier)
+        central.sl4f.bts_lib.forgetDevice(identifier)
 
         raise signals.TestPass("Success")
 
diff --git a/acts_tests/tests/google/fuchsia/bt/command_input.py b/acts_tests/tests/google/fuchsia/bt/command_input.py
index c993ac0..e3f77ac 100644
--- a/acts_tests/tests/google/fuchsia/bt/command_input.py
+++ b/acts_tests/tests/google/fuchsia/bt/command_input.py
@@ -112,11 +112,11 @@
     def _find_unique_id_over_le(self):
         scan_filter = {"name_substring": self.target_device_name}
         self.unique_mac_addr_id = None
-        self.pri_dut.gattc_lib.bleStartBleScan(scan_filter)
+        self.pri_dut.sl4f.gattc_lib.bleStartBleScan(scan_filter)
         tries = 10
         for i in range(tries):
             time.sleep(self.bt_scan_poll_timer)
-            scan_res = self.pri_dut.gattc_lib.bleGetDiscoveredDevices(
+            scan_res = self.pri_dut.sl4f.gattc_lib.bleGetDiscoveredDevices(
             )['result']
             for device in scan_res:
                 name, did, connectable = device["name"], device["id"], device[
@@ -129,18 +129,18 @@
                     break
             if self.unique_mac_addr_id:
                 break
-        self.pri_dut.gattc_lib.bleStopBleScan()
+        self.pri_dut.sl4f.gattc_lib.bleStopBleScan()
 
     def _find_unique_id_over_bt_control(self):
         self.unique_mac_addr_id = None
         self.bt_control_devices = []
-        self.pri_dut.bts_lib.requestDiscovery(True)
+        self.pri_dut.sl4f.bts_lib.requestDiscovery(True)
         tries = 10
         for i in range(tries):
             if self.unique_mac_addr_id:
                 break
             time.sleep(self.bt_scan_poll_timer)
-            device_list = self.pri_dut.bts_lib.getKnownRemoteDevices(
+            device_list = self.pri_dut.sl4f.bts_lib.getKnownRemoteDevices(
             )['result']
             for id_dict in device_list:
                 device = device_list[id_dict]
@@ -159,7 +159,7 @@
                             "Successfully found device: name, id, address: {}, {}, {}"
                             .format(name, did, address))
                         break
-        self.pri_dut.bts_lib.requestDiscovery(False)
+        self.pri_dut.sl4f.bts_lib.requestDiscovery(False)
 
     def do_tool_take_bt_snoop_log(self, custom_name):
         """
@@ -438,7 +438,7 @@
         else:
             scan_response = adv_data
 
-        result = self.pri_dut.ble_lib.bleStartBleAdvertising(
+        result = self.pri_dut.sl4f.ble_lib.bleStartBleAdvertising(
             adv_data, scan_response, self.ble_adv_interval, connectable)
         self.log.info("Result of starting advertisement: {}".format(result))
         self.ble_adv_data_manufacturer_data = None
@@ -481,7 +481,7 @@
         """
         cmd = "Stop a connectable LE advertisement"
         try:
-            self.pri_dut.ble_lib.bleStopBleAdvertising()
+            self.pri_dut.sl4f.ble_lib.bleStopBleAdvertising()
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
 
@@ -508,7 +508,7 @@
         cmd = "Connect to a LE peripheral by input ID."
         try:
 
-            connection_status = self.pri_dut.gattc_lib.bleConnectToPeripheral(
+            connection_status = self.pri_dut.sl4f.gattc_lib.bleConnectToPeripheral(
                 line)
             self.log.info("Connection status: {}".format(
                 pprint.pformat(connection_status)))
@@ -537,7 +537,7 @@
                 except Exception as err:
                     self.log.info("Failed to scan or find device.")
                     return
-            connection_status = self.pri_dut.gattc_lib.bleConnectToPeripheral(
+            connection_status = self.pri_dut.sl4f.gattc_lib.bleConnectToPeripheral(
                 self.unique_mac_addr_id)
             self.log.info("Connection status: {}".format(
                 pprint.pformat(connection_status)))
@@ -563,12 +563,12 @@
                     return
             for i in range(int(line)):
                 self.log.info("Running iteration {}".format(i + 1))
-                connection_status = self.pri_dut.gattc_lib.bleConnectToPeripheral(
+                connection_status = self.pri_dut.sl4f.gattc_lib.bleConnectToPeripheral(
                     self.unique_mac_addr_id)
                 self.log.info("Connection status: {}".format(
                     pprint.pformat(connection_status)))
                 time.sleep(4)
-                disc_status = self.pri_dut.gattc_lib.bleDisconnectPeripheral(
+                disc_status = self.pri_dut.sl4f.gattc_lib.bleDisconnectPeripheral(
                     self.unique_mac_addr_id)
                 self.log.info("Disconnect status: {}".format(disc_status))
                 time.sleep(3)
@@ -585,7 +585,7 @@
         """
         cmd = "Disconenct from LE peripheral."
         try:
-            disconnect_status = self.pri_dut.gattc_lib.bleDisconnectPeripheral(
+            disconnect_status = self.pri_dut.sl4f.gattc_lib.bleDisconnectPeripheral(
                 self.unique_mac_addr_id)
             self.log.info("Disconnect status: {}".format(disconnect_status))
         except Exception as err:
@@ -606,16 +606,17 @@
         cmd = "List services from LE peripheral."
         try:
 
-            services = self.pri_dut.gattc_lib.listServices(
+            services = self.pri_dut.sl4f.gattc_lib.listServices(
                 self.unique_mac_addr_id)
             self.log.info("Discovered Services: \n{}".format(
                 pprint.pformat(services)))
             discover_characteristics = self.str_to_bool(discover_chars)
             if discover_chars:
                 for service in services.get('result'):
-                    self.pri_dut.gattc_lib.connectToService(
+                    self.pri_dut.sl4f.gattc_lib.connectToService(
                         self.unique_mac_addr_id, service.get('id'))
-                    chars = self.pri_dut.gattc_lib.discoverCharacteristics()
+                    chars = self.pri_dut.sl4f.gattc_lib.discoverCharacteristics(
+                    )
                     self.log.info("Discovered chars:\n{}".format(
                         pprint.pformat(chars)))
 
@@ -634,8 +635,8 @@
         """
         cmd = "GATT client connect to GATT server service."
         try:
-            self.pri_dut.gattc_lib.connectToService(self.unique_mac_addr_id,
-                                                    int(line))
+            self.pri_dut.sl4f.gattc_lib.connectToService(
+                self.unique_mac_addr_id, int(line))
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
 
@@ -649,7 +650,7 @@
         """
         cmd = "Discover and list characteristics from a GATT server."
         try:
-            chars = self.pri_dut.gattc_lib.discoverCharacteristics()
+            chars = self.pri_dut.sl4f.gattc_lib.discoverCharacteristics()
             self.log.info("Discovered chars:\n{}".format(
                 pprint.pformat(chars)))
         except Exception as err:
@@ -666,14 +667,14 @@
         """
         cmd = "Read all characteristics from the GATT service."
         try:
-            services = self.pri_dut.gattc_lib.listServices(
+            services = self.pri_dut.sl4f.gattc_lib.listServices(
                 self.unique_mac_addr_id)
             for service in services['result']:
                 service_id = service['id']
                 service_uuid = service['uuid_type']
-                self.pri_dut.gattc_lib.connectToService(
+                self.pri_dut.sl4f.gattc_lib.connectToService(
                     self.unique_mac_addr_id, service_id)
-                chars = self.pri_dut.gattc_lib.discoverCharacteristics()
+                chars = self.pri_dut.sl4f.gattc_lib.discoverCharacteristics()
                 print("Reading chars in service uuid: {}".format(service_uuid))
 
                 for char in chars['result']:
@@ -682,7 +683,7 @@
                     # quick char filter for apple-4 test... remove later
                     print("found uuid {}".format(char_uuid))
                     try:
-                        self.pri_dut.gattc_lib.enableNotifyCharacteristic(
+                        self.pri_dut.sl4f.gattc_lib.enableNotifyCharacteristic(
                             char_id)
                     except Exception as err:
                         print("error enabling notification")
@@ -700,14 +701,14 @@
         """
         cmd = "Read all characteristics from the GATT service."
         try:
-            services = self.pri_dut.gattc_lib.listServices(
+            services = self.pri_dut.sl4f.gattc_lib.listServices(
                 self.unique_mac_addr_id)
             for service in services['result']:
                 service_id = service['id']
                 service_uuid = service['uuid_type']
-                self.pri_dut.gattc_lib.connectToService(
+                self.pri_dut.sl4f.gattc_lib.connectToService(
                     self.unique_mac_addr_id, service_id)
-                chars = self.pri_dut.gattc_lib.discoverCharacteristics()
+                chars = self.pri_dut.sl4f.gattc_lib.discoverCharacteristics()
                 print("Reading chars in service uuid: {}".format(service_uuid))
 
                 for char in chars['result']:
@@ -715,7 +716,7 @@
                     char_uuid = char['uuid_type']
                     try:
                         read_val =  \
-                            self.pri_dut.gattc_lib.readCharacteristicById(
+                            self.pri_dut.sl4f.gattc_lib.readCharacteristicById(
                                 char_id)
                         print("  Characteristic uuid / Value: {} / {}".format(
                             char_uuid, read_val['result']))
@@ -725,7 +726,6 @@
                         print("    str val: {}".format(str_value))
                     except Exception as err:
                         print(err)
-                        pass
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
 
@@ -740,14 +740,14 @@
         """
         cmd = "Read all descriptors from the GATT service."
         try:
-            services = self.pri_dut.gattc_lib.listServices(
+            services = self.pri_dut.sl4f.gattc_lib.listServices(
                 self.unique_mac_addr_id)
             for service in services['result']:
                 service_id = service['id']
                 service_uuid = service['uuid_type']
-                self.pri_dut.gattc_lib.connectToService(
+                self.pri_dut.sl4f.gattc_lib.connectToService(
                     self.unique_mac_addr_id, service_id)
-                chars = self.pri_dut.gattc_lib.discoverCharacteristics()
+                chars = self.pri_dut.sl4f.gattc_lib.discoverCharacteristics()
                 print("Reading descs in service uuid: {}".format(service_uuid))
 
                 for char in chars['result']:
@@ -759,7 +759,7 @@
                         desc_id = desc["id"]
                         desc_uuid = desc["uuid_type"]
                     try:
-                        read_val = self.pri_dut.gattc_lib.readDescriptorById(
+                        read_val = self.pri_dut.sl4f.gattc_lib.readDescriptorById(
                             desc_id)
                         print("    Descriptor uuid / Value: {} / {}".format(
                             desc_uuid, read_val['result']))
@@ -792,14 +792,14 @@
             write_value = []
             for i in range(int(size)):
                 write_value.append(i % 256)
-            services = self.pri_dut.gattc_lib.listServices(
+            services = self.pri_dut.sl4f.gattc_lib.listServices(
                 self.unique_mac_addr_id)
             for service in services['result']:
                 service_id = service['id']
                 service_uuid = service['uuid_type']
-                self.pri_dut.gattc_lib.connectToService(
+                self.pri_dut.sl4f.gattc_lib.connectToService(
                     self.unique_mac_addr_id, service_id)
-                chars = self.pri_dut.gattc_lib.discoverCharacteristics()
+                chars = self.pri_dut.sl4f.gattc_lib.discoverCharacteristics()
                 print("Writing descs in service uuid: {}".format(service_uuid))
 
                 for char in chars['result']:
@@ -811,7 +811,7 @@
                         desc_id = desc["id"]
                         desc_uuid = desc["uuid_type"]
                     try:
-                        write_val = self.pri_dut.gattc_lib.writeDescriptorById(
+                        write_val = self.pri_dut.sl4f.gattc_lib.writeDescriptorById(
                             desc_id, offset, write_value)
                         print("    Descriptor uuid / Result: {} / {}".format(
                             desc_uuid, write_val['result']))
@@ -840,14 +840,14 @@
                 return
             offset = int(args[0])
             max_bytes = int(args[1])
-            services = self.pri_dut.ble_lib.bleListServices(
+            services = self.pri_dut.sl4f.ble_lib.bleListServices(
                 self.unique_mac_addr_id)
             for service in services['result']:
                 service_id = service['id']
                 service_uuid = service['uuid_type']
-                self.pri_dut.gattc_lib.connectToService(
+                self.pri_dut.sl4f.gattc_lib.connectToService(
                     self.unique_mac_addr_id, service_id)
-                chars = self.pri_dut.gattc_lib.discoverCharacteristics()
+                chars = self.pri_dut.sl4f.gattc_lib.discoverCharacteristics()
                 print("Reading descs in service uuid: {}".format(service_uuid))
 
                 for char in chars['result']:
@@ -859,7 +859,7 @@
                         desc_id = desc["id"]
                         desc_uuid = desc["uuid_type"]
                     try:
-                        read_val = self.pri_dut.gattc_lib.readLongDescriptorById(
+                        read_val = self.pri_dut.sl4f.gattc_lib.readLongDescriptorById(
                             desc_id, offset, max_bytes)
                         print("    Descriptor uuid / Result: {} / {}".format(
                             desc_uuid, read_val['result']))
@@ -888,21 +888,21 @@
                 return
             offset = int(args[0])
             max_bytes = int(args[1])
-            services = self.pri_dut.ble_lib.bleListServices(
+            services = self.pri_dut.sl4f.ble_lib.bleListServices(
                 self.unique_mac_addr_id)
             for service in services['result']:
                 service_id = service['id']
                 service_uuid = service['uuid_type']
-                self.pri_dut.gattc_lib.connectToService(
+                self.pri_dut.sl4f.gattc_lib.connectToService(
                     self.unique_mac_addr_id, service_id)
-                chars = self.pri_dut.gattc_lib.discoverCharacteristics()
+                chars = self.pri_dut.sl4f.gattc_lib.discoverCharacteristics()
                 print("Reading chars in service uuid: {}".format(service_uuid))
 
                 for char in chars['result']:
                     char_id = char['id']
                     char_uuid = char['uuid_type']
                     try:
-                        read_val = self.pri_dut.gattc_lib.readLongCharacteristicById(
+                        read_val = self.pri_dut.sl4f.gattc_lib.readLongCharacteristicById(
                             char_id, offset, max_bytes)
                         print("    Char uuid / Result: {} / {}".format(
                             char_uuid, read_val['result']))
@@ -936,27 +936,26 @@
             write_value = []
             for i in range(size):
                 write_value.append(i % 256)
-            services = self.pri_dut.gattc_lib.listServices(
+            services = self.pri_dut.sl4f.gattc_lib.listServices(
                 self.unique_mac_addr_id)
             for service in services['result']:
                 service_id = service['id']
                 service_uuid = service['uuid_type']
-                self.pri_dut.gattc_lib.connectToService(
+                self.pri_dut.sl4f.gattc_lib.connectToService(
                     self.unique_mac_addr_id, service_id)
-                chars = self.pri_dut.gattc_lib.discoverCharacteristics()
+                chars = self.pri_dut.sl4f.gattc_lib.discoverCharacteristics()
                 print("Writing chars in service uuid: {}".format(service_uuid))
 
                 for char in chars['result']:
                     char_id = char['id']
                     char_uuid = char['uuid_type']
                     try:
-                        write_result = self.pri_dut.gattc_lib.writeCharById(
+                        write_result = self.pri_dut.sl4f.gattc_lib.writeCharById(
                             char_id, offset, write_value)
                         print("  Characteristic uuid write result: {} / {}".
                               format(char_uuid, write_result['result']))
                     except Exception as err:
                         print("error writing char {}".format(err))
-                        pass
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
 
@@ -982,14 +981,14 @@
             write_value = []
             for i in range(size):
                 write_value.append(i % 256)
-            services = self.pri_dut.gattc_lib.listServices(
+            services = self.pri_dut.sl4f.gattc_lib.listServices(
                 self.unique_mac_addr_id)
             for service in services['result']:
                 service_id = service['id']
                 service_uuid = service['uuid_type']
-                self.pri_dut.gattc_lib.connectToService(
+                self.pri_dut.sl4f.gattc_lib.connectToService(
                     self.unique_mac_addr_id, service_id)
-                chars = self.pri_dut.gattc_lib.discoverCharacteristics()
+                chars = self.pri_dut.sl4f.gattc_lib.discoverCharacteristics()
                 print("Reading chars in service uuid: {}".format(service_uuid))
 
                 for char in chars['result']:
@@ -997,7 +996,7 @@
                     char_uuid = char['uuid_type']
                     try:
                         write_result = \
-                            self.pri_dut.gattc_lib.writeCharByIdWithoutResponse(
+                            self.pri_dut.sl4f.gattc_lib.writeCharByIdWithoutResponse(
                                 char_id, write_value)
                         print("  Characteristic uuid write result: {} / {}".
                               format(char_uuid, write_result['result']))
@@ -1325,7 +1324,7 @@
             scan_filter = {"name_substring": ""}
             if line:
                 scan_filter = {"name_substring": line}
-            self.pri_dut.gattc_lib.bleStartBleScan(scan_filter)
+            self.pri_dut.sl4f.gattc_lib.bleStartBleScan(scan_filter)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
 
@@ -1338,7 +1337,7 @@
         """
         cmd = "Stops a BLE scan and returns discovered devices."
         try:
-            scan_results = self.pri_dut.gattc_lib.bleStopBleScan()
+            scan_results = self.pri_dut.sl4f.gattc_lib.bleStopBleScan()
             self._update_scan_results(scan_results)
             self.log.info(pprint.pformat(scan_results))
         except Exception as err:
@@ -1353,7 +1352,8 @@
         """
         cmd = "Get discovered LE devices of an active scan."
         try:
-            scan_results = self.pri_dut.gattc_lib.bleGetDiscoveredDevices()
+            scan_results = self.pri_dut.sl4f.gattc_lib.bleGetDiscoveredDevices(
+            )
             self._update_scan_results(scan_results)
             self.log.info(pprint.pformat(scan_results))
         except Exception as err:
@@ -1372,7 +1372,7 @@
         """
         cmd = "Close active GATT server."
         try:
-            result = self.pri_dut.gatts_lib.closeServer()
+            result = self.pri_dut.sl4f.gatts_lib.closeServer()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -1401,7 +1401,7 @@
         """
         cmd = "Setup GATT Server Database Based of pre-defined dictionaries"
         try:
-            scan_results = self.pri_dut.gatts_lib.publishServer(
+            scan_results = self.pri_dut.sl4f.gatts_lib.publishServer(
                 gatt_test_database.GATT_SERVER_DB_MAPPING.get(line))
             self.log.info(scan_results)
         except Exception as err:
@@ -1542,7 +1542,7 @@
             if len(options) > 1:
                 input_capabilities = options[0]
                 output_capabilities = options[1]
-            result = self.pri_dut.bts_lib.acceptPairing(
+            result = self.pri_dut.sl4f.bts_lib.acceptPairing(
                 input_capabilities, output_capabilities)
             self.log.info(result)
         except Exception as err:
@@ -1563,7 +1563,8 @@
         try:
             self.log.info("Forgetting device id: {}".format(
                 self.unique_mac_addr_id))
-            result = self.pri_dut.bts_lib.forgetDevice(self.unique_mac_addr_id)
+            result = self.pri_dut.sl4f.bts_lib.forgetDevice(
+                self.unique_mac_addr_id)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -1618,7 +1619,7 @@
         """
         cmd = "Change whether the Bluetooth Controller is in active."
         try:
-            result = self.pri_dut.bts_lib.requestDiscovery(
+            result = self.pri_dut.sl4f.bts_lib.requestDiscovery(
                 self.str_to_bool(discover))
             self.log.info(result)
         except Exception as err:
@@ -1635,7 +1636,7 @@
         cmd = "Get a list of known devices."
         self.bt_control_devices = []
         try:
-            device_list = self.pri_dut.bts_lib.getKnownRemoteDevices(
+            device_list = self.pri_dut.sl4f.bts_lib.getKnownRemoteDevices(
             )['result']
             for id_dict in device_list:
                 device = device_list[id_dict]
@@ -1655,14 +1656,15 @@
         """
         cmd = "Forget all known devices."
         try:
-            device_list = self.pri_dut.bts_lib.getKnownRemoteDevices(
+            device_list = self.pri_dut.sl4f.bts_lib.getKnownRemoteDevices(
             )['result']
             for device in device_list:
                 d = device_list[device]
                 if d['bonded'] or d['connected']:
                     self.log.info("Unbonding deivce: {}".format(d))
                     self.log.info(
-                        self.pri_dut.bts_lib.forgetDevice(d['id'])['result'])
+                        self.pri_dut.sl4f.bts_lib.forgetDevice(
+                            d['id'])['result'])
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
 
@@ -1680,7 +1682,7 @@
         """
         cmd = "Connect to device under test."
         try:
-            result = self.pri_dut.bts_lib.connectDevice(
+            result = self.pri_dut.sl4f.bts_lib.connectDevice(
                 self.unique_mac_addr_id)
             self.log.info(result)
         except Exception as err:
@@ -1708,7 +1710,7 @@
         """
         cmd = "Connect to device id based on pre-defined inputs."
         try:
-            result = self.pri_dut.bts_lib.connectDevice(device_id)
+            result = self.pri_dut.sl4f.bts_lib.connectDevice(device_id)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -1738,7 +1740,8 @@
             for device in self.bt_control_devices:
                 if device_name is device['name']:
 
-                    result = self.pri_dut.bts_lib.connectDevice(device['id'])
+                    result = self.pri_dut.sl4f.bts_lib.connectDevice(
+                        device['id'])
                     self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -1757,7 +1760,7 @@
         """
         cmd = "Disconnect to device under test."
         try:
-            result = self.pri_dut.bts_lib.disconnectDevice(
+            result = self.pri_dut.sl4f.bts_lib.disconnectDevice(
                 self.unique_mac_addr_id)
             self.log.info(result)
         except Exception as err:
@@ -1804,7 +1807,7 @@
         """
         cmd = "Input pairing pin to the Fuchsia device."
         try:
-            result = self.pri_dut.bts_lib.inputPairingPin(line)['result']
+            result = self.pri_dut.sl4f.bts_lib.inputPairingPin(line)['result']
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -1820,7 +1823,7 @@
         """
         cmd = "Get the pairing pin from the Fuchsia device."
         try:
-            result = self.pri_dut.bts_lib.getPairingPin()['result']
+            result = self.pri_dut.sl4f.bts_lib.getPairingPin()['result']
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -1852,31 +1855,31 @@
         ]
 
         try:
-            self.pri_dut.sdp_lib.addSearch(
+            self.pri_dut.sl4f.sdp_lib.addSearch(
                 attributes, int(sig_uuid_constants['AudioSource'], 16))
-            self.pri_dut.sdp_lib.addSearch(
+            self.pri_dut.sl4f.sdp_lib.addSearch(
                 attributes, int(sig_uuid_constants['A/V_RemoteControl'], 16))
-            self.pri_dut.sdp_lib.addSearch(attributes,
-                                           int(sig_uuid_constants['PANU'], 16))
-            self.pri_dut.sdp_lib.addSearch(
+            self.pri_dut.sl4f.sdp_lib.addSearch(
+                attributes, int(sig_uuid_constants['PANU'], 16))
+            self.pri_dut.sl4f.sdp_lib.addSearch(
                 attributes, int(sig_uuid_constants['SerialPort'], 16))
-            self.pri_dut.sdp_lib.addSearch(
+            self.pri_dut.sl4f.sdp_lib.addSearch(
                 attributes, int(sig_uuid_constants['DialupNetworking'], 16))
-            self.pri_dut.sdp_lib.addSearch(
+            self.pri_dut.sl4f.sdp_lib.addSearch(
                 attributes, int(sig_uuid_constants['OBEXObjectPush'], 16))
-            self.pri_dut.sdp_lib.addSearch(
+            self.pri_dut.sl4f.sdp_lib.addSearch(
                 attributes, int(sig_uuid_constants['OBEXFileTransfer'], 16))
-            self.pri_dut.sdp_lib.addSearch(
+            self.pri_dut.sl4f.sdp_lib.addSearch(
                 attributes, int(sig_uuid_constants['Headset'], 16))
-            self.pri_dut.sdp_lib.addSearch(
+            self.pri_dut.sl4f.sdp_lib.addSearch(
                 attributes, int(sig_uuid_constants['HandsfreeAudioGateway'],
                                 16))
-            self.pri_dut.sdp_lib.addSearch(
+            self.pri_dut.sl4f.sdp_lib.addSearch(
                 attributes, int(sig_uuid_constants['Handsfree'], 16))
-            self.pri_dut.sdp_lib.addSearch(
+            self.pri_dut.sl4f.sdp_lib.addSearch(
                 attributes, int(sig_uuid_constants['SIM_Access'], 16))
             for i in range(int(num_of_records)):
-                result = self.pri_dut.sdp_lib.addService(
+                result = self.pri_dut.sl4f.sdp_lib.addService(
                     sdp_pts_record_list[i])
                 self.log.info(result)
         except Exception as err:
@@ -1892,7 +1895,7 @@
         """
         cmd = "Cleanup SDP objects."
         try:
-            result = self.pri_dut.sdp_lib.cleanUp()
+            result = self.pri_dut.sl4f.sdp_lib.cleanUp()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -1907,7 +1910,7 @@
         """
         cmd = "Initialize profile proxy objects for adding SDP records"
         try:
-            result = self.pri_dut.sdp_lib.init()
+            result = self.pri_dut.sl4f.sdp_lib.init()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -1953,9 +1956,8 @@
         cmd = "Connect l2cap"
         try:
             info = line.split()
-            result = self.pri_dut.sdp_lib.connectL2cap(self.unique_mac_addr_id,
-                                                       int(info[0], 16),
-                                                       info[1])
+            result = self.pri_dut.sl4f.sdp_lib.connectL2cap(
+                self.unique_mac_addr_id, int(info[0], 16), info[1])
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -1982,7 +1984,7 @@
         try:
             if not initiator_delay:
                 initiator_delay = None
-            result = self.pri_dut.avdtp_lib.init(initiator_delay)
+            result = self.pri_dut.sl4f.avdtp_lib.init(initiator_delay)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -1997,8 +1999,7 @@
         """
         cmd = "Kill A2DP service"
         try:
-            result = self.pri_dut.control_daemon("bt-a2dp.cmx", "stop")
-            self.log.info(result)
+            self.pri_dut.start_v1_component("bt-a2dp")
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
 
@@ -2012,7 +2013,7 @@
         """
         cmd = "AVDTP get connected peers"
         try:
-            result = self.pri_dut.avdtp_lib.getConnectedPeers()
+            result = self.pri_dut.sl4f.avdtp_lib.getConnectedPeers()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2030,7 +2031,7 @@
         """
         cmd = "Send AVDTP set configuration to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.setConfiguration(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.setConfiguration(int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2048,7 +2049,7 @@
         """
         cmd = "Send AVDTP get configuration to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.getConfiguration(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.getConfiguration(int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2066,7 +2067,7 @@
         """
         cmd = "Send AVDTP get capabilities to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.getCapabilities(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.getCapabilities(int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2084,7 +2085,8 @@
         """
         cmd = "Send AVDTP get all capabilities to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.getAllCapabilities(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.getAllCapabilities(
+                int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2102,7 +2104,8 @@
         """
         cmd = "Send AVDTP reconfigure stream to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.reconfigureStream(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.reconfigureStream(
+                int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2120,7 +2123,7 @@
         """
         cmd = "Send AVDTP suspend stream to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.suspendStream(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.suspendStream(int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2138,7 +2141,8 @@
         """
         cmd = "Send AVDTP suspend reconfigure to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.suspendAndReconfigure(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.suspendAndReconfigure(
+                int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2156,7 +2160,7 @@
         """
         cmd = "Send AVDTP release stream to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.releaseStream(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.releaseStream(int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2174,7 +2178,7 @@
         """
         cmd = "Send AVDTP establish stream to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.establishStream(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.establishStream(int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2192,7 +2196,7 @@
         """
         cmd = "Send AVDTP start stream to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.startStream(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.startStream(int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2210,7 +2214,7 @@
         """
         cmd = "Send AVDTP abort stream to connected peer"
         try:
-            result = self.pri_dut.avdtp_lib.abortStream(int(peer_id))
+            result = self.pri_dut.sl4f.avdtp_lib.abortStream(int(peer_id))
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2225,7 +2229,7 @@
         """
         cmd = "Remove AVDTP service"
         try:
-            result = self.pri_dut.avdtp_lib.removeService()
+            result = self.pri_dut.sl4f.avdtp_lib.removeService()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2243,7 +2247,7 @@
         """
         cmd = "Start audio capture"
         try:
-            result = self.pri_dut.audio_lib.startOutputSave()
+            result = self.pri_dut.sl4f.audio_lib.startOutputSave()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2258,7 +2262,7 @@
         """
         cmd = "Stop audio capture"
         try:
-            result = self.pri_dut.audio_lib.stopOutputSave()
+            result = self.pri_dut.sl4f.audio_lib.stopOutputSave()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2274,7 +2278,7 @@
         cmd = "Get audio capture"
         try:
             save_path = "{}/{}".format(self.pri_dut.log_path, "audio.raw")
-            result = self.pri_dut.audio_lib.getOutputAudio(save_path)
+            result = self.pri_dut.sl4f.audio_lib.getOutputAudio(save_path)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
 
@@ -2297,19 +2301,19 @@
         cmd = "5 min audio capture test"
         input("Press Enter once Source device is streaming audio file")
         try:
-            result = self.pri_dut.audio_lib.startOutputSave()
+            result = self.pri_dut.sl4f.audio_lib.startOutputSave()
             self.log.info(result)
             for i in range(5):
                 print("Minutes left: {}".format(10 - i))
                 time.sleep(60)
-            result = self.pri_dut.audio_lib.stopOutputSave()
+            result = self.pri_dut.sl4f.audio_lib.stopOutputSave()
             log_time = int(time.time())
             save_path = "{}/{}".format(self.pri_dut.log_path,
                                        "{}_audio.raw".format(log_time))
             analysis_path = "{}/{}".format(
                 self.pri_dut.log_path,
                 "{}_audio_analysis.txt".format(log_time))
-            result = self.pri_dut.audio_lib.getOutputAudio(save_path)
+            result = self.pri_dut.sl4f.audio_lib.getOutputAudio(save_path)
 
             channels = 1
             try:
@@ -2343,7 +2347,7 @@
         """
         cmd = "Initialize HFP proxy"
         try:
-            result = self.pri_dut.hfp_lib.init()
+            result = self.pri_dut.sl4f.hfp_lib.init()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2358,7 +2362,7 @@
         """
         cmd = "Remove HFP service"
         try:
-            result = self.pri_dut.hfp_lib.removeService()
+            result = self.pri_dut.sl4f.hfp_lib.removeService()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2375,7 +2379,7 @@
         """
         cmd = "Lists connected peers"
         try:
-            result = self.pri_dut.hfp_lib.listPeers()
+            result = self.pri_dut.sl4f.hfp_lib.listPeers()
             self.log.info(pprint.pformat(result))
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2394,7 +2398,7 @@
         cmd = "Set the active peer"
         try:
             peer_id = int(line.strip())
-            result = self.pri_dut.hfp_lib.setActivePeer(peer_id)
+            result = self.pri_dut.sl4f.hfp_lib.setActivePeer(peer_id)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2411,7 +2415,7 @@
         """
         cmd = "Lists all calls"
         try:
-            result = self.pri_dut.hfp_lib.listCalls()
+            result = self.pri_dut.sl4f.hfp_lib.listCalls()
             self.log.info(pprint.pformat(result))
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2444,7 +2448,8 @@
                     "Exactly three command line arguments required: <remote> <state> <direction>"
                 )
             remote, state, direction = info[0], info[1], info[2]
-            result = self.pri_dut.hfp_lib.newCall(remote, state, direction)
+            result = self.pri_dut.sl4f.hfp_lib.newCall(remote, state,
+                                                       direction)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2464,7 +2469,7 @@
         cmd = "Simulates an incoming call"
         try:
             remote = line.strip()
-            result = self.pri_dut.hfp_lib.initiateIncomingCall(remote)
+            result = self.pri_dut.sl4f.hfp_lib.initiateIncomingCall(remote)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2485,7 +2490,8 @@
         cmd = "Simulates an incoming call"
         try:
             remote = line.strip()
-            result = self.pri_dut.hfp_lib.initiateIncomingWaitingCall(remote)
+            result = self.pri_dut.sl4f.hfp_lib.initiateIncomingWaitingCall(
+                remote)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2504,7 +2510,7 @@
         cmd = "Simulates an outgoing call"
         try:
             remote = line.strip()
-            result = self.pri_dut.hfp_lib.initiateOutgoingCall(remote)
+            result = self.pri_dut.sl4f.hfp_lib.initiateOutgoingCall(remote)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2523,7 +2529,7 @@
         cmd = "Set the specified call to active"
         try:
             call_id = int(line.strip())
-            result = self.pri_dut.hfp_lib.setCallActive(call_id)
+            result = self.pri_dut.sl4f.hfp_lib.setCallActive(call_id)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2542,7 +2548,7 @@
         cmd = "Set the specified call to held"
         try:
             call_id = int(line.strip())
-            result = self.pri_dut.hfp_lib.setCallHeld(call_id)
+            result = self.pri_dut.sl4f.hfp_lib.setCallHeld(call_id)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2561,7 +2567,7 @@
         cmd = "Set the specified call to terminated"
         try:
             call_id = int(line.strip())
-            result = self.pri_dut.hfp_lib.setCallTerminated(call_id)
+            result = self.pri_dut.sl4f.hfp_lib.setCallTerminated(call_id)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2580,7 +2586,7 @@
         cmd = "Set the specified call to TransferredToAg"
         try:
             call_id = int(line.strip())
-            result = self.pri_dut.hfp_lib.setCallTransferredToAg(call_id)
+            result = self.pri_dut.sl4f.hfp_lib.setCallTransferredToAg(call_id)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2599,7 +2605,7 @@
         cmd = "Set the active peer's speaker gain"
         try:
             value = int(line.strip())
-            result = self.pri_dut.hfp_lib.setSpeakerGain(value)
+            result = self.pri_dut.sl4f.hfp_lib.setSpeakerGain(value)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2618,7 +2624,7 @@
         cmd = "Set the active peer's microphone gain"
         try:
             value = int(line.strip())
-            result = self.pri_dut.hfp_lib.setMicrophoneGain(value)
+            result = self.pri_dut.sl4f.hfp_lib.setMicrophoneGain(value)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2639,7 +2645,7 @@
         cmd = "Sets the simulated network service status reported by the call manager"
         try:
             value = line.strip() == "true"
-            result = self.pri_dut.hfp_lib.setServiceAvailable(value)
+            result = self.pri_dut.sl4f.hfp_lib.setServiceAvailable(value)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2660,7 +2666,7 @@
         cmd = "Sets the simulated roaming status reported by the call manager"
         try:
             value = line.strip() == "true"
-            result = self.pri_dut.hfp_lib.setRoaming(value)
+            result = self.pri_dut.sl4f.hfp_lib.setRoaming(value)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2682,7 +2688,7 @@
         cmd = "Sets the simulated signal strength reported by the call manager"
         try:
             value = int(line.strip())
-            result = self.pri_dut.hfp_lib.setSignalStrength(value)
+            result = self.pri_dut.sl4f.hfp_lib.setSignalStrength(value)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2702,7 +2708,7 @@
         cmd = "Sets the subscriber number reported by the call manager"
         try:
             value = line.strip()
-            result = self.pri_dut.hfp_lib.setSubscriberNumber(value)
+            result = self.pri_dut.sl4f.hfp_lib.setSubscriberNumber(value)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2722,7 +2728,7 @@
         cmd = "Sets the operator value reported by the call manager"
         try:
             value = line.strip()
-            result = self.pri_dut.hfp_lib.setOperator(value)
+            result = self.pri_dut.sl4f.hfp_lib.setOperator(value)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2743,7 +2749,7 @@
         cmd = "Sets the noise reduction/echo cancelation support reported by the call manager"
         try:
             value = line.strip() == "true"
-            result = self.pri_dut.hfp_lib.setNrecSupport(value)
+            result = self.pri_dut.sl4f.hfp_lib.setNrecSupport(value)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2764,7 +2770,7 @@
         cmd = "Set the battery level reported by the call manager"
         try:
             value = int(line.strip())
-            result = self.pri_dut.hfp_lib.setBatteryLevel(value)
+            result = self.pri_dut.sl4f.hfp_lib.setBatteryLevel(value)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2784,7 +2790,7 @@
         cmd = "Sets the last dialed number in the call manager."
         try:
             number = line.strip()
-            result = self.pri_dut.hfp_lib.setLastDialed(number)
+            result = self.pri_dut.sl4f.hfp_lib.setLastDialed(number)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2799,7 +2805,7 @@
         """
         cmd = "Clears the last dialed number in the call manager."
         try:
-            result = self.pri_dut.hfp_lib.clearLastDialed()
+            result = self.pri_dut.sl4f.hfp_lib.clearLastDialed()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2825,7 +2831,8 @@
                     "Exactly two command line arguments required: <location> <number>"
                 )
             location, number = info[0], info[1]
-            result = self.pri_dut.hfp_lib.setMemoryLocation(location, number)
+            result = self.pri_dut.sl4f.hfp_lib.setMemoryLocation(
+                location, number)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2845,7 +2852,7 @@
         cmd = "Sets a memory location to point to a remote number."
         try:
             location = line.strip()
-            result = self.pri_dut.hfp_lib.clearMemoryLocation(location)
+            result = self.pri_dut.sl4f.hfp_lib.clearMemoryLocation(location)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2870,7 +2877,7 @@
                     "Exactly two command line arguments required: <number> <status>"
                 )
             number, status = info[0], int(info[1])
-            result = self.pri_dut.hfp_lib.setDialResult(number, status)
+            result = self.pri_dut.sl4f.hfp_lib.setDialResult(number, status)
             self.log.info(pprint.pformat(result))
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2885,7 +2892,7 @@
         """
         cmd = "Get the call manager's state"
         try:
-            result = self.pri_dut.hfp_lib.getState()
+            result = self.pri_dut.sl4f.hfp_lib.getState()
             self.log.info(pprint.pformat(result))
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2906,7 +2913,8 @@
         cmd = "Set the Service Level Connection (SLC) behavior"
         try:
             autoconnect = line.strip().lower() == "true"
-            result = self.pri_dut.hfp_lib.setConnectionBehavior(autoconnect)
+            result = self.pri_dut.sl4f.hfp_lib.setConnectionBehavior(
+                autoconnect)
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2924,7 +2932,7 @@
         """
         cmd = "Initialize RFCOMM proxy"
         try:
-            result = self.pri_dut.rfcomm_lib.init()
+            result = self.pri_dut.sl4f.rfcomm_lib.init()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2939,7 +2947,7 @@
         """
         cmd = "Remove RFCOMM service"
         try:
-            result = self.pri_dut.rfcomm_lib.removeService()
+            result = self.pri_dut.sl4f.rfcomm_lib.removeService()
             self.log.info(result)
         except Exception as err:
             self.log.error(FAILURE.format(cmd, err))
@@ -2955,7 +2963,7 @@
         """
         cmd = "Disconnect the RFCOMM Session"
         try:
-            result = self.pri_dut.rfcomm_lib.disconnectSession(
+            result = self.pri_dut.sl4f.rfcomm_lib.disconnectSession(
                 self.unique_mac_addr_id)
             self.log.info(result)
         except Exception as err:
@@ -2973,7 +2981,7 @@
         cmd = "Make an outgoing RFCOMM connection"
         try:
             server_channel_number = int(line.strip())
-            result = self.pri_dut.rfcomm_lib.connectRfcommChannel(
+            result = self.pri_dut.sl4f.rfcomm_lib.connectRfcommChannel(
                 self.unique_mac_addr_id, server_channel_number)
             self.log.info(result)
         except Exception as err:
@@ -2991,7 +2999,7 @@
         cmd = "Close the RFCOMM channel"
         try:
             server_channel_number = int(line.strip())
-            result = self.pri_dut.rfcomm_lib.disconnectRfcommChannel(
+            result = self.pri_dut.sl4f.rfcomm_lib.disconnectRfcommChannel(
                 self.unique_mac_addr_id, server_channel_number)
             self.log.info(result)
         except Exception as err:
@@ -3009,7 +3017,7 @@
         cmd = "Send a remote line status update for the RFCOMM channel"
         try:
             server_channel_number = int(line.strip())
-            result = self.pri_dut.rfcomm_lib.sendRemoteLineStatus(
+            result = self.pri_dut.sl4f.rfcomm_lib.sendRemoteLineStatus(
                 self.unique_mac_addr_id, server_channel_number)
             self.log.info(result)
         except Exception as err:
@@ -3033,7 +3041,7 @@
                 )
             server_channel_number = int(info[0])
             data = info[1]
-            result = self.pri_dut.rfcomm_lib.writeRfcomm(
+            result = self.pri_dut.sl4f.rfcomm_lib.writeRfcomm(
                 self.unique_mac_addr_id, server_channel_number, data)
             self.log.info(result)
         except Exception as err:
diff --git a/acts_tests/tests/google/fuchsia/bt/ep/BtFuchsiaEPTest.py b/acts_tests/tests/google/fuchsia/bt/ep/BtFuchsiaEPTest.py
index e01afd5..733fa9b 100644
--- a/acts_tests/tests/google/fuchsia/bt/ep/BtFuchsiaEPTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/ep/BtFuchsiaEPTest.py
@@ -20,6 +20,7 @@
 
 from acts import signals
 from acts.base_test import BaseTestClass
+from acts.controllers.fuchsia_lib.ssh import FuchsiaSSHError
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.bt_test_utils import generate_id_by_size
 from acts_contrib.test_utils.fuchsia.bt_test_utils import bredr_scan_for_device_by_name
@@ -49,7 +50,7 @@
     def setup_class(self):
         super().setup_class()
         for fd in self.fuchsia_devices:
-            fd.bts_lib.initBluetoothSys()
+            fd.sl4f.bts_lib.initBluetoothSys()
         self.pri_dut = self.fuchsia_devices[0]
         self.sec_dut = self.fuchsia_devices[1]
 
@@ -57,7 +58,7 @@
         for fd in self.fuchsia_devices:
             fd.take_bug_report(test_name, begin_time)
         self._unbond_all_known_devices()
-        self.sec_dut.ble_lib.bleStopBleAdvertising()
+        self.sec_dut.sl4f.ble_lib.bleStopBleAdvertising()
         self._kill_media_services()
 
     def teardown_class(self):
@@ -68,12 +69,11 @@
         """
         ssh_timeout = 30
         for fd in self.fuchsia_devices:
-            fd.send_command_ssh("killall bt-a2dp*",
-                                timeout=ssh_timeout,
-                                skip_status_code_check=True)
-            fd.send_command_ssh("killall bt-avrcp*",
-                                timeout=ssh_timeout,
-                                skip_status_code_check=True)
+            try:
+                fd.ssh.run("killall bt-a2dp*", timeout_sec=ssh_timeout)
+                fd.ssh.run("killall bt-avrcp*", timeout_sec=ssh_timeout)
+            except FuchsiaSSHError:
+                pass
 
     def _unbond_all_known_devices(self):
         """For all Fuchsia devices, unbond any known pairings.
@@ -103,14 +103,14 @@
         Priority: 0
         """
 
-        self.sec_dut.ble_lib.bleStartBleAdvertising(
+        self.sec_dut.sl4f.ble_lib.bleStartBleAdvertising(
             self.test_adv_data, self.test_scan_response,
             self.ble_advertise_interval, self.test_connectable)
 
         device = le_scan_for_device_by_name(self.pri_dut, self.log,
                                             self.adv_name,
                                             self.scan_timeout_seconds)
-        self.sec_dut.ble_lib.bleStopBleAdvertising()
+        self.sec_dut.sl4f.ble_lib.bleStopBleAdvertising()
         if device is None:
             raise signals.TestFailure("Scanner unable to find advertisement.")
         raise signals.TestPass("Success")
@@ -141,9 +141,9 @@
         self._unbond_all_known_devices()
 
         source_device_name = generate_id_by_size(10)
-        self.pri_dut.bts_lib.setName(source_device_name)
+        self.pri_dut.sl4f.bts_lib.setName(source_device_name)
 
-        self.sec_dut.ble_lib.bleStartBleAdvertising(
+        self.sec_dut.sl4f.ble_lib.bleStartBleAdvertising(
             self.test_adv_data, self.test_scan_response,
             self.ble_advertise_interval, self.test_connectable)
 
@@ -153,7 +153,7 @@
         if device is None:
             raise signals.TestFailure("Scanner unable to find advertisement.")
 
-        connect_result = self.pri_dut.gattc_lib.bleConnectToPeripheral(
+        connect_result = self.pri_dut.sl4f.gattc_lib.bleConnectToPeripheral(
             device["id"])
         if connect_result.get("error") is not None:
             raise signals.TestFailure("GATT Connection failed with: {}".format(
@@ -172,8 +172,8 @@
         security_level = "ENCRYPTED"
         non_bondable = False
         transport = 2  #LE
-        self.pri_dut.bts_lib.pair(device["id"], security_level, non_bondable,
-                                  transport)
+        self.pri_dut.sl4f.bts_lib.pair(device["id"], security_level,
+                                       non_bondable, transport)
 
         services = None
         if not verify_device_state_by_name(self.pri_dut, self.log,
@@ -187,14 +187,14 @@
             raise signals.TestFailure(
                 "Failed to pair device {}.".format(source_device_name))
 
-        disconnect_result = self.pri_dut.gattc_lib.bleDisconnectPeripheral(
+        disconnect_result = self.pri_dut.sl4f.gattc_lib.bleDisconnectPeripheral(
             device["id"])
         if disconnect_result.get("error") is not None:
             raise signals.TestFailure(
                 "GATT Disconnection failed with: {}".format(
                     connect_result.get("error")))
 
-        self.sec_dut.ble_lib.bleStopBleAdvertising()
+        self.sec_dut.sl4f.ble_lib.bleStopBleAdvertising()
 
         # TODO: Setup Proper GATT server and verify services published are found
 
@@ -236,22 +236,22 @@
         source_device_name = generate_id_by_size(10)
         target_device_name = generate_id_by_size(10)
 
-        self.pri_dut.bts_lib.setName(source_device_name)
-        self.sec_dut.bts_lib.setName(target_device_name)
+        self.pri_dut.sl4f.bts_lib.setName(source_device_name)
+        self.sec_dut.sl4f.bts_lib.setName(target_device_name)
 
         input_capabilities = "NONE"
         output_capabilities = "NONE"
 
         # Initialize a2dp on both devices.
-        self.pri_dut.avdtp_lib.init()
-        self.sec_dut.avdtp_lib.init()
+        self.pri_dut.sl4f.avdtp_lib.init()
+        self.sec_dut.sl4f.avdtp_lib.init()
 
-        self.pri_dut.bts_lib.acceptPairing(input_capabilities,
-                                           output_capabilities)
+        self.pri_dut.sl4f.bts_lib.acceptPairing(input_capabilities,
+                                                output_capabilities)
 
-        self.sec_dut.bts_lib.acceptPairing(input_capabilities,
-                                           output_capabilities)
-        self.sec_dut.bts_lib.setDiscoverable(True)
+        self.sec_dut.sl4f.bts_lib.acceptPairing(input_capabilities,
+                                                output_capabilities)
+        self.sec_dut.sl4f.bts_lib.setDiscoverable(True)
 
         unique_mac_addr_id = bredr_scan_for_device_by_name(
             self.pri_dut, self.log, target_device_name,
@@ -261,7 +261,8 @@
             raise signals.TestFailure(
                 "Failed to find device {}.".format(target_device_name))
 
-        connect_result = self.pri_dut.bts_lib.connectDevice(unique_mac_addr_id)
+        connect_result = self.pri_dut.sl4f.bts_lib.connectDevice(
+            unique_mac_addr_id)
         if connect_result.get("error") is not None:
             raise signals.TestFailure("Failed to connect with {}.".format(
                 connect_result.get("error")))
@@ -272,9 +273,9 @@
         security_level = "NONE"
         bondable = True
         transport = 1  #BREDR
-        pair_result = self.pri_dut.bts_lib.pair(unique_mac_addr_id,
-                                                security_level, bondable,
-                                                transport)
+        pair_result = self.pri_dut.sl4f.bts_lib.pair(unique_mac_addr_id,
+                                                     security_level, bondable,
+                                                     transport)
         if pair_result.get("error") is not None:
             raise signals.TestFailure("Failed to pair with {}.".format(
                 pair_result.get("error")))
diff --git a/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py b/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py
index da7d641..cf6ff41 100644
--- a/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py
@@ -32,7 +32,6 @@
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.bt.bt_test_utils import generate_id_by_size
 from acts_contrib.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
-import time
 
 
 class GattConnectionStressTest(BaseTestClass):
@@ -66,28 +65,28 @@
         }
         scan_response = None
         connectable = True
-        self.fuchsia_server_dut.ble_lib.bleStartBleAdvertising(
+        self.fuchsia_server_dut.sl4f.ble_lib.bleStartBleAdvertising(
             adv_data, scan_response, self.ble_advertise_interval, connectable)
         device = le_scan_for_device_by_name(self.fuchsia_client_dut, self.log,
                                             adv_name,
                                             self.scan_timeout_seconds)
         if device is None:
             raise signals.TestFailure("Scanner unable to find advertisement.")
-        connect_result = self.fuchsia_client_dut.gattc_lib.bleConnectToPeripheral(
+        connect_result = self.fuchsia_client_dut.sl4f.gattc_lib.bleConnectToPeripheral(
             device["id"])
         if connect_result.get("error") is not None:
             raise signals.TestFailure(
                 self.gatt_connect_err_message.format(
                     connect_result.get("error")))
         self.log.info("Connection Successful...")
-        disconnect_result = self.fuchsia_client_dut.gattc_lib.bleDisconnectPeripheral(
+        disconnect_result = self.fuchsia_client_dut.sl4f.gattc_lib.bleDisconnectPeripheral(
             device["id"])
         if disconnect_result.get("error") is not None:
             raise signals.TestFailure(
                 self.gatt_disconnect_err_message.format(
                     connect_result.get("error")))
         self.log.info("Disconnection Successful...")
-        self.fuchsia_server_dut.ble_lib.bleStopBleAdvertising()
+        self.fuchsia_server_dut.sl4f.ble_lib.bleStopBleAdvertising()
 
     # TODO: add @test_tracker_info(uuid='')
     def test_connect_reconnect_n_iterations_over_le(self):
diff --git a/acts_tests/tests/google/fuchsia/bt/gatt/GattServerSetupTest.py b/acts_tests/tests/google/fuchsia/bt/gatt/GattServerSetupTest.py
index 41a4cf2..9d6b9a2 100644
--- a/acts_tests/tests/google/fuchsia/bt/gatt/GattServerSetupTest.py
+++ b/acts_tests/tests/google/fuchsia/bt/gatt/GattServerSetupTest.py
@@ -36,7 +36,7 @@
         self.fuchsia_dut = self.fuchsia_devices[0]
 
     def setup_database(self, database):
-        setup_result = self.fuchsia_dut.gatts_lib.publishServer(database)
+        setup_result = self.fuchsia_dut.sl4f.gatts_lib.publishServer(database)
         if setup_result.get("error") is None:
             signals.TestPass(setup_result.get("result"))
         else:
@@ -44,7 +44,7 @@
                 self.err_message.format(setup_result.get("error")))
 
     def test_teardown(self):
-        self.fuchsia_dut.gatts_lib.closeServer()
+        self.fuchsia_dut.sl4f.gatts_lib.closeServer()
 
     @test_tracker_info(uuid='25f3463b-b6bd-408b-9924-f18ed3b9bbe2')
     def test_single_primary_service(self):
diff --git a/acts_tests/tests/google/fuchsia/dhcp/Dhcpv4InteropTest.py b/acts_tests/tests/google/fuchsia/dhcp/Dhcpv4InteropTest.py
index 1a11658..24029f4 100644
--- a/acts_tests/tests/google/fuchsia/dhcp/Dhcpv4InteropTest.py
+++ b/acts_tests/tests/google/fuchsia/dhcp/Dhcpv4InteropTest.py
@@ -14,8 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import ipaddress
-import itertools
 import random
 import time
 import re
@@ -29,11 +27,10 @@
 from acts.controllers.ap_lib.hostapd_utils import generate_random_password
 from acts.controllers.utils_lib.commands import ip
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 
 
-class Dhcpv4InteropFixture(AbstractDeviceWlanDeviceBaseTest):
+class Dhcpv4InteropFixture(WifiBaseTest):
     """Test helpers for validating DHCPv4 Interop
 
     Test Bed Requirement:
@@ -42,9 +39,6 @@
     """
     access_point: AccessPoint
 
-    def __init__(self, controllers):
-        WifiBaseTest.__init__(self, controllers)
-
     def setup_class(self):
         super().setup_class()
         if 'dut' in self.user_params:
@@ -169,7 +163,7 @@
             'seconds.' % (interface, timeout))
         timeout = time.time() + timeout
         while time.time() < timeout:
-            ip_addrs = self.dut.get_interface_ip_addresses(interface)
+            ip_addrs = self.dut.device.get_interface_ip_addresses(interface)
 
             if len(ip_addrs['ipv4_private']) > 0:
                 ip = ip_addrs['ipv4_private'][0]
@@ -203,11 +197,9 @@
         self.log.debug('DHCP Configuration:\n' +
                        dhcp_conf.render_config_file() + "\n")
 
-        dhcp_logs_before = self.access_point.get_dhcp_logs()
+        dhcp_logs_before = self.access_point.get_dhcp_logs().split('\n')
         self.access_point.start_dhcp(dhcp_conf=dhcp_conf)
         self.connect(ap_params=ap_params)
-        dhcp_logs_after = self.access_point.get_dhcp_logs()
-        dhcp_logs = dhcp_logs_after.replace(dhcp_logs_before, '')
 
         # Typical log lines look like:
         # dhcpd[26695]: DHCPDISCOVER from f8:0f:f9:3d:ce:d1 via wlan1
@@ -221,6 +213,11 @@
             self.log.warn(dhcp_logs)
             asserts.fail(f'DUT failed to get an IP address')
 
+        # Get updates to DHCP logs
+        dhcp_logs = self.access_point.get_dhcp_logs()
+        for line in dhcp_logs_before:
+            dhcp_logs = dhcp_logs.replace(line, '')
+
         expected_string = f'DHCPDISCOVER from'
         asserts.assert_equal(
             dhcp_logs.count(expected_string), 1,
@@ -351,6 +348,7 @@
 
 
 class Dhcpv4DuplicateAddressTest(Dhcpv4InteropFixture):
+
     def setup_test(self):
         super().setup_test()
         self.extra_addresses = []
@@ -361,7 +359,6 @@
         super().teardown_test()
         for ip in self.extra_addresses:
             self.ap_ip_cmd.remove_ipv4_address(self.ap_params['id'], ip)
-            pass
 
     def test_duplicate_address_assignment(self):
         """It's possible for a DHCP server to assign an address that already exists on the network.
@@ -442,30 +439,46 @@
     OPT_NUM_DOMAIN_SEARCH = 119
     OPT_NUM_DOMAIN_NAME = 15
 
-    def setup_class(self):
-        super().setup_class()
+    def setup_generated_tests(self):
+        self._generate_dhcp_options()
+
+        test_args = []
+        for test in self.DHCP_OPTIONS:
+            for option_list in self.DHCP_OPTIONS[test]:
+                test_args.append(({
+                    'dhcp_options': option_list,
+                    'dhcp_parameters': {}
+                }, ))
+
+        self.generate_tests(test_logic=self.run_test_case_expect_dhcp_success,
+                            name_func=self.generate_test_name,
+                            arg_sets=test_args)
+
+    def generate_test_name(self, settings):
+        return settings["dhcp_options"]["test_name"]
+
+    def _generate_dhcp_options(self):
         self.DHCP_OPTIONS = {
             'domain-name-tests': [{
-                'domain-name':
-                '"example.invalid"',
-                'dhcp-parameter-request-list':
-                self.OPT_NUM_DOMAIN_NAME
+                'domain-name': '"example.invalid"',
+                'dhcp-parameter-request-list': self.OPT_NUM_DOMAIN_NAME,
+                'test_name': "test_domain_name_invalid_tld"
             }, {
-                'domain-name':
-                '"example.test"',
-                'dhcp-parameter-request-list':
-                self.OPT_NUM_DOMAIN_NAME
+                'domain-name': '"example.test"',
+                'dhcp-parameter-request-list': self.OPT_NUM_DOMAIN_NAME,
+                'test_name': "test_domain_name_valid_tld"
             }],
             'domain-search-tests': [{
                 'domain-search':
                 '"example.invalid"',
                 'dhcp-parameter-request-list':
-                self.OPT_NUM_DOMAIN_SEARCH
+                self.OPT_NUM_DOMAIN_SEARCH,
+                'test_name':
+                "test_domain_search_invalid_tld"
             }, {
-                'domain-search':
-                '"example.test"',
-                'dhcp-parameter-request-list':
-                self.OPT_NUM_DOMAIN_SEARCH
+                'domain-search': '"example.test"',
+                'dhcp-parameter-request-list': self.OPT_NUM_DOMAIN_SEARCH,
+                'test_name': "test_domain_search_valid_tld"
             }]
         }
 
@@ -496,45 +509,7 @@
             'domain-search':
             long_dns_setting,
             'dhcp-parameter-request-list':
-            self.OPT_NUM_DOMAIN_SEARCH
+            self.OPT_NUM_DOMAIN_SEARCH,
+            'test_name':
+            "test_max_sized_message",
         })
-
-    def test_domain_names(self):
-        test_list = []
-        for option_list in self.DHCP_OPTIONS['domain-name-tests']:
-            test_list.append({
-                'dhcp_options': option_list,
-                'dhcp_parameters': {}
-            })
-        self.run_generated_testcases(self.run_test_case_expect_dhcp_success,
-                                     settings=test_list)
-
-    def test_search_domains(self):
-        test_list = []
-        for option_list in self.DHCP_OPTIONS['domain-search-tests']:
-            test_list.append({
-                'dhcp_options': option_list,
-                'dhcp_parameters': {}
-            })
-        self.run_generated_testcases(self.run_test_case_expect_dhcp_success,
-                                     settings=test_list)
-
-    def test_large_messages(self):
-        test_list = []
-        for option_list in self.DHCP_OPTIONS['max-message-size-tests']:
-            test_list.append({
-                'dhcp_options': option_list,
-                'dhcp_parameters': {}
-            })
-        self.run_generated_testcases(self.run_test_case_expect_dhcp_success,
-                                     settings=test_list)
-
-    def test_dns(self):
-        pass
-
-    def test_ignored_options_singularly(self):
-        pass
-
-    def test_all_combinations(self):
-        # TODO: test all the combinations above
-        pass
diff --git a/acts_tests/tests/google/fuchsia/examples/Sl4fSanityTest.py b/acts_tests/tests/google/fuchsia/examples/Sl4fSanityTest.py
index 91d664e..db9fd07 100644
--- a/acts_tests/tests/google/fuchsia/examples/Sl4fSanityTest.py
+++ b/acts_tests/tests/google/fuchsia/examples/Sl4fSanityTest.py
@@ -20,26 +20,29 @@
 """
 from acts.base_test import BaseTestClass
 
-import os
-import uuid
+from acts import asserts
 
-from acts import signals
-from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
+from acts.controllers.fuchsia_device import FuchsiaDevice
 
 
 class Sl4fSanityTest(BaseTestClass):
+    fuchsia_devices: list[FuchsiaDevice]
+
     def setup_class(self):
         super().setup_class()
 
-        success_str = ("Congratulations! Fuchsia controllers have been "
-                       "initialized successfully!")
-        err_str = ("Sorry, please try verifying FuchsiaDevice is in your "
-                   "config file and try again.")
-        if len(self.fuchsia_devices) > 0:
-            self.log.info(success_str)
-        else:
-            raise signals.TestAbortClass("err_str")
+        asserts.abort_class_if(
+            len(self.fuchsia_devices) == 0,
+            "Sorry, please try verifying FuchsiaDevice is in your config file and try again."
+        )
+
+        self.log.info(
+            "Congratulations! Fuchsia controllers have been initialized successfully!"
+        )
 
     def test_example(self):
+        for fuchsia_device in self.fuchsia_devices:
+            res = fuchsia_device.sl4f.netstack_lib.netstackListInterfaces()
+            self.log.info(res)
         self.log.info("Congratulations! You've run your first test.")
         return True
diff --git a/acts_tests/tests/google/fuchsia/flash/FlashTest.py b/acts_tests/tests/google/fuchsia/flash/FlashTest.py
index 03662f2..b625c2e 100644
--- a/acts_tests/tests/google/fuchsia/flash/FlashTest.py
+++ b/acts_tests/tests/google/fuchsia/flash/FlashTest.py
@@ -19,37 +19,29 @@
 fuchsia_devices.
 """
 from acts import asserts
+from acts import signals
 from acts.base_test import BaseTestClass
-from acts.controllers.fuchsia_lib.base_lib import DeviceOffline
 from acts.utils import get_device
 
 MAX_FLASH_ATTEMPTS = 3
 
 
 class FlashTest(BaseTestClass):
+
     def setup_class(self):
         super().setup_class()
-        success_str = ("Congratulations! Fuchsia controllers have been "
-                       "initialized successfully!")
-        err_str = ("Sorry, please try verifying FuchsiaDevice is in your "
-                   "config file and try again.")
-        if len(self.fuchsia_devices) > 0:
-            self.log.info(success_str)
-        else:
-            raise signals.TestAbortClass("err_str")
+        self.failed_to_get_version = False
 
     def teardown_class(self):
-        try:
-            dut = get_device(self.fuchsia_devices, 'DUT')
-            version = dut.version()
-            self.record_data({'sponge_properties': {
-                'DUT_VERSION': version,
-            }})
-            self.log.info("DUT version found: {}".format(version))
-        except ValueError as err:
-            self.log.warn("Failed to determine DUT: %s" % err)
-        except DeviceOffline as err:
-            self.log.warn("Failed to get DUT's version: %s" % err)
+        # Verify that FlashTest successfully reported the DUT version. This is
+        # working around a flaw in ACTS where signals.TestAbortAll does not
+        # report any errors.
+        #
+        # TODO(http://b/253515812): This has been fixed in Mobly already. Remove
+        # teardown_class and change "TestError" to "abort_all" in
+        # test_flash_devices once we move to Mobly.
+        if self.failed_to_get_version:
+            asserts.abort_all('Failed to get DUT version')
 
         return super().teardown_class()
 
@@ -84,11 +76,14 @@
                     device.reboot(reboot_type='hard',
                                   testbed_pdus=self.pdu_devices)
 
-    def test_report_dut_version(self):
-        """Empty test to ensure the version of the DUT is reported in the Sponge
-        results in the case when flashing the device is not necessary.
-
-        Useful for when flashing the device is not necessary; specify ACTS to
-        only run this test from the test class.
-        """
-        pass
+        # Report the new Fuchsia version
+        try:
+            dut = get_device(self.fuchsia_devices, 'DUT')
+            version = dut.version()
+            self.record_data({'sponge_properties': {
+                'DUT_VERSION': version,
+            }})
+            self.log.info("DUT version found: {}".format(version))
+        except Exception as e:
+            self.failed_to_get_version = True
+            raise signals.TestError(f'Failed to get DUT version: {e}') from e
diff --git a/acts_tests/tests/google/fuchsia/hardware_power_statecontrol/HardwarePowerStatecontrolTest.py b/acts_tests/tests/google/fuchsia/hardware_power_statecontrol/HardwarePowerStatecontrolTest.py
deleted file mode 100644
index ad63090..0000000
--- a/acts_tests/tests/google/fuchsia/hardware_power_statecontrol/HardwarePowerStatecontrolTest.py
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-from acts import signals
-from acts.base_test import BaseTestClass
-from acts import asserts
-"""
-This is a stress test for Fuchsia reboot tests.
-
-Custom Params:
-reboot_stress_test_iterations
-
-    Example:
-    "reboot_stress_test_iterations": 50
-
-Setup:
-One Fuchsia device
-"""
-
-
-class HardwarePowerStatecontrolStressTest(BaseTestClass):
-    default_iterations = 100
-
-    def setup_class(self):
-        super().setup_class()
-        self.dut = self.fuchsia_devices[0]
-        self.default_iterations = self.user_params.get(
-            "reboot_stress_test_iterations", self.default_iterations)
-
-    def test_suspend_reboot(self):
-        """Verify suspend reboot functionality
-
-        Steps:
-        1. Send a suspend reboot command to DUT.
-
-        Expected Result:
-        No errors in sending a reboot and device comes back online.
-
-        Returns:
-          signals.TestPass if no errors
-          signals.TestFailure if there are any errors during the test.
-
-        TAGS: Hardware
-        Priority: 1
-        """
-        try:
-            self.dut.reboot()
-        except Exception as err:
-            signals.TestFailure("Failed with err: {}".format(err))
-        signals.TestPass("Pass")
-
-    def test_suspend_reboot_stress(self):
-        """Verify suspend reboot functionality N times.
-
-        Steps:
-        1. Send a suspend reboot command to DUT N times
-
-        Expected Result:
-        No errors in sending a reboot and device comes back online.
-
-        Returns:
-          signals.TestPass if no errors
-          signals.TestFailure if there are any errors during the test.
-
-        TAGS: Hardware
-        Priority: 1
-        """
-        for i in range(self.default_iterations):
-            try:
-                self.dut.reboot()
-            except Exception as err:
-                signals.TestFailure("Failed with err: {}".format(err))
-            self.log.info("Iteration {} successful".format(i + 1))
-
-        signals.TestPass("Pass")
diff --git a/acts_tests/tests/google/fuchsia/hwinfo/HwinfoTest.py b/acts_tests/tests/google/fuchsia/hwinfo/HwinfoTest.py
deleted file mode 100644
index 163bd36..0000000
--- a/acts_tests/tests/google/fuchsia/hwinfo/HwinfoTest.py
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-from acts import signals
-from acts.base_test import BaseTestClass
-from acts import asserts
-
-
-class HwinfoTest(BaseTestClass):
-    def setup_class(self):
-        super().setup_class()
-        self.dut = self.fuchsia_devices[0]
-
-    def test_get_device_info(self):
-        """Verify that getting the hardware device information of a Fuchsia
-        device does not return an error.
-
-        Steps:
-        1. Get the hardware device info of a Fuchsia device.
-
-        Expected Result:
-        No errors in getting the hardware info.
-
-        Returns:
-          signals.TestPass if no errors
-          signals.TestFailure if there are any errors during the test.
-
-        TAGS: Hardware
-        Priority: 2
-        """
-        result = self.dut.hwinfo_lib.getDeviceInfo()
-        if result.get("error") is None:
-            self.log.info("HW info found: {}".format(result))
-            signals.TestPass(result.get("result"))
-        else:
-            signals.TestFailure(result.get("error"))
-
-    def test_get_product_info(self):
-        """Verify that getting the hardware product information of a Fuchsia
-        device does not return an error.
-
-        Steps:
-        1. Get the hardware product info of a Fuchsia device.
-
-        Expected Result:
-        No errors in getting the hardware product info.
-
-        Returns:
-          signals.TestPass if no errors
-          signals.TestFailure if there are any errors during the test.
-
-        TAGS: Hardware
-        Priority: 2
-        """
-        result = self.dut.hwinfo_lib.getProductInfo()
-        if result.get("error") is None:
-            self.log.info("HW info found: {}".format(result))
-            signals.TestPass(result.get("result"))
-        else:
-            signals.TestFailure(result.get("error"))
-
-    def test_get_board_info(self):
-        """Verify that getting the hardware board information of a Fuchsia
-        device does not return an error.
-
-        Steps:
-        1. Get the hardware board info of a Fuchsia device.
-
-        Expected Result:
-        No errors in getting the hardware board info.
-
-        Returns:
-          signals.TestPass if no errors
-          signals.TestFailure if there are any errors during the test.
-
-        TAGS: Hardware
-        Priority: 2
-        """
-        result = self.dut.hwinfo_lib.getBoardInfo()
-        if result.get("error") is None:
-            self.log.info("HW info found: {}".format(result))
-            signals.TestPass(result.get("result"))
-        else:
-            signals.TestFailure(result.get("error"))
diff --git a/acts_tests/tests/google/fuchsia/input/TouchTest.py b/acts_tests/tests/google/fuchsia/input/TouchTest.py
deleted file mode 100644
index 7467139..0000000
--- a/acts_tests/tests/google/fuchsia/input/TouchTest.py
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env python3
-#
-#   Copyright 2020 - The Android Open Source Project
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#       http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-
-import time
-
-from acts import asserts
-from acts import signals
-from acts.base_test import BaseTestClass
-from acts.libs.proc.job import Error
-
-TEST_TIME_SECONDS = 5
-TEST_POLL_TIME_SECONDS = 0.1
-
-
-class TouchTest(BaseTestClass):
-    def setup_class(self):
-        super().setup_class()
-        self.fd = self.fuchsia_devices[0]
-
-    def test_touch_reports(self):
-        """Prints touch events for the next 5 seconds.
-
-        This test requires a 'touch_test_params' object to be specified in the
-        device config. touch_test_params will be used by the facade to find the
-        touch device, and may include 'vendor_id' and 'product_id' values.
-        """
-        asserts.skip_if('touch_tests_params' not in self.user_params,
-                        'touch_tests_params not specified in the config')
-
-        # Make a call to the facade to get it to establish a connection to the
-        # touch device. This ensures that the first second of touch events don't
-        # get missed.
-        result = self.fd.input_report_lib.getDescriptor(
-            **self.user_params['touch_tests_params'])
-
-        self.log.info('Printing touch events for the next %d seconds...' %
-                      TEST_TIME_SECONDS)
-
-        end_time = time.time() + TEST_TIME_SECONDS
-        while time.time() < end_time:
-            time.sleep(TEST_POLL_TIME_SECONDS)
-
-            result = self.fd.input_report_lib.getReports(
-                **self.user_params['touch_tests_params'])
-            asserts.assert_true(result['error'] is None,
-                                'GetReports failed: %s' % result['error'])
-
-            for event in result['result']:
-                contacts = event['touch']['contacts']
-                touch = ', '.join([
-                    '(%d, %d)' % (t['position_x'], t['position_y'])
-                    for t in event['touch']['contacts']
-                ]) or '(none)'
-                self.log.info('touch event @ %d: %s' %
-                              (event['event_time'], touch))
diff --git a/acts_tests/tests/google/fuchsia/logging/FuchsiaLoggingTest.py b/acts_tests/tests/google/fuchsia/logging/FuchsiaLoggingTest.py
index 28d6998..bed05eb 100644
--- a/acts_tests/tests/google/fuchsia/logging/FuchsiaLoggingTest.py
+++ b/acts_tests/tests/google/fuchsia/logging/FuchsiaLoggingTest.py
@@ -20,27 +20,28 @@
 
 
 class FuchsiaLoggingTest(BaseTestClass):
+
     def setup_class(self):
         super().setup_class()
         self.dut = self.fuchsia_devices[0]
         self.message = "Logging Test"
 
     def test_log_err(self):
-        result = self.dut.logging_lib.logE(self.message)
+        result = self.dut.sl4f.logging_lib.logE(self.message)
         if result.get("error") is None:
             signals.TestPass(result.get("result"))
         else:
             signals.TestFailure(result.get("error"))
 
     def test_log_info(self):
-        result = self.dut.logging_lib.logI(self.message)
+        result = self.dut.sl4f.logging_lib.logI(self.message)
         if result.get("error") is None:
             signals.TestPass(result.get("result"))
         else:
             signals.TestFailure(result.get("error"))
 
     def test_log_warn(self):
-        result = self.dut.logging_lib.logW(self.message)
+        result = self.dut.sl4f.logging_lib.logW(self.message)
         if result.get("error") is None:
             signals.TestPass(result.get("result"))
         else:
diff --git a/acts_tests/tests/google/fuchsia/netstack/NetstackIfaceTest.py b/acts_tests/tests/google/fuchsia/netstack/NetstackIfaceTest.py
index 7033156..bf4b70b 100644
--- a/acts_tests/tests/google/fuchsia/netstack/NetstackIfaceTest.py
+++ b/acts_tests/tests/google/fuchsia/netstack/NetstackIfaceTest.py
@@ -35,10 +35,10 @@
         self.dut = self.fuchsia_devices[0]
 
     def _enable_all_interfaces(self):
-        interfaces = self.dut.netstack_lib.netstackListInterfaces()
+        interfaces = self.dut.sl4f.netstack_lib.netstackListInterfaces()
         for item in interfaces.get("result"):
             identifier = item.get('id')
-            self.dut.netstack_lib.enableInterface(identifier)
+            self.dut.sl4f.netstack_lib.enableInterface(identifier)
 
     def setup_test(self):
         # Always make sure all interfaces listed are in an up state.
@@ -66,7 +66,7 @@
         TAGS: Netstack
         Priority: 1
         """
-        interfaces = self.dut.netstack_lib.netstackListInterfaces()
+        interfaces = self.dut.sl4f.netstack_lib.netstackListInterfaces()
         if interfaces.get('error') is not None:
             raise signals.TestFailure("Failed with {}".format(
                 interfaces.get('error')))
@@ -99,7 +99,7 @@
         """
 
         def get_wlan_interfaces():
-            result = self.dut.netstack_lib.netstackListInterfaces()
+            result = self.dut.sl4f.netstack_lib.netstackListInterfaces()
             if (error := result.get('error')):
                 raise signals.TestFailure(
                     f'unable to list interfaces: {error}')
@@ -118,7 +118,7 @@
 
         # Disable the interfaces.
         for identifier in interface_ids:
-            result = self.dut.netstack_lib.disableInterface(identifier)
+            result = self.dut.sl4f.netstack_lib.disableInterface(identifier)
             if (error := result.get('error')):
                 raise signals.TestFailure(
                     f'failed to disable wlan interface {identifier}: {error}')
@@ -142,7 +142,7 @@
 
         # Re-enable the interfaces.
         for identifier in disabled_interface_ids:
-            result = self.dut.netstack_lib.enableInterface(identifier)
+            result = self.dut.sl4f.netstack_lib.enableInterface(identifier)
             if (error := result.get('error')):
                 raise signals.TestFailure(
                     f'failed to enable wlan interface {identifier}: {error}')
diff --git a/acts_tests/tests/google/fuchsia/netstack/ToggleWlanInterfaceStressTest.py b/acts_tests/tests/google/fuchsia/netstack/ToggleWlanInterfaceStressTest.py
index 9b60998..4709fa4 100644
--- a/acts_tests/tests/google/fuchsia/netstack/ToggleWlanInterfaceStressTest.py
+++ b/acts_tests/tests/google/fuchsia/netstack/ToggleWlanInterfaceStressTest.py
@@ -21,6 +21,7 @@
 
 
 class ToggleWlanInterfaceStressTest(BaseTestClass):
+
     def setup_class(self):
         dut = self.user_params.get('dut', None)
         if dut:
@@ -67,11 +68,11 @@
             if not self.dut.destroy_wlan_interface(wlan_interfaces[0]):
                 raise signals.TestFailure("Failed to destroy WLAN interface")
             # Really make sure it is dead
-            self.fuchsia_devices[0].send_command_ssh(
-                "wlan iface del {}".format(wlan_interfaces[0]))
+            self.fuchsia_devices[0].ssh.run(
+                f"wlan iface del {wlan_interfaces[0]}")
             # Grace period
             time.sleep(2)
-            self.fuchsia_devices[0].send_command_ssh(
+            self.fuchsia_devices[0].ssh.run(
                 'wlan iface new --phy 0 --role Client')
             end_time = time.time() + 300
             while time.time() < end_time:
diff --git a/acts_tests/tests/google/fuchsia/ram/RamTest.py b/acts_tests/tests/google/fuchsia/ram/RamTest.py
deleted file mode 100644
index 2b11e01..0000000
--- a/acts_tests/tests/google/fuchsia/ram/RamTest.py
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-import os
-import time
-import uuid
-
-from acts import asserts, signals
-from acts.base_test import BaseTestClass
-from acts.libs.proc.job import Error
-from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
-
-# Some number bigger than the minimum 512k:
-MEMORY_CYCLES_TO_MEASURE = 1024 * 1024
-# Taken from the commandline tool.
-#TODO(48254): Convert driver to fill in these values automatically.
-MEMORY_CHANNELS = [1, 2, 0x600110, 0x1F0000, 0, 0, 0, 0]
-
-
-class RamTest(BaseTestClass):
-    def setup_class(self):
-        super().setup_class()
-        self.fd = self.fuchsia_devices[0]
-
-    def test_bandwidth(self):
-        """Test MeasureBandwidth FIDL calls.
-
-        There should be no noticable effect. This test will only run
-        if the device config has a 'ram_tests' entry.
-        """
-        asserts.skip_if('ram_tests' not in self.user_params,
-                        'ram_tests not specified in the config')
-
-        result = self.fd.ram_lib.measureBandwidth(MEMORY_CYCLES_TO_MEASURE,
-                                                  MEMORY_CHANNELS)
-        asserts.assert_true(
-            result['error'] is None,
-            "MeasureBandwidth failed with: {}".format(result['error']))
-
-    def test_get_ddr_results(self):
-        """Test GetDdrWindowingResults FIDL calls.
-
-        There should be no noticable effect. This test will only run
-        if the device config has a 'ram_tests' entry.
-        """
-        asserts.skip_if('ram_tests' not in self.user_params,
-                        'ram_tests not specified in the config')
-
-        result = self.fd.ram_lib.getDdrWindowingResults()
-        asserts.assert_true(
-            result['error'] is None,
-            "GetDdrWindowingResults failed with: {}".format(result['error']))
diff --git a/acts_tests/tests/google/fuchsia/wlan/compliance/VapeInteropTest.py b/acts_tests/tests/google/fuchsia/wlan/compliance/VapeInteropTest.py
index 962c1bf..24169d9 100644
--- a/acts_tests/tests/google/fuchsia/wlan/compliance/VapeInteropTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/compliance/VapeInteropTest.py
@@ -17,15 +17,13 @@
 from acts import asserts
 from acts import utils
 from acts.controllers.access_point import setup_ap
-from acts.controllers.ap_lib import hostapd_ap_preset
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib.hostapd_security import Security
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 
 
-class VapeInteropTest(AbstractDeviceWlanDeviceBaseTest):
+class VapeInteropTest(WifiBaseTest):
     """Tests interoperability with mock third party AP profiles.
 
     Test Bed Requirement:
diff --git a/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyCompliance11ACTest.py b/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyCompliance11ACTest.py
index 04adfab..f18a28d 100644
--- a/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyCompliance11ACTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyCompliance11ACTest.py
@@ -15,16 +15,13 @@
 #   limitations under the License.
 
 import itertools
-import re
 
 from acts import asserts
 from acts import utils
 from acts.controllers.access_point import setup_ap
 from acts.controllers.ap_lib.hostapd_security import Security
 from acts.controllers.ap_lib import hostapd_constants
-from acts.controllers.ap_lib import hostapd_config
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts.utils import rand_ascii_str
 
@@ -87,6 +84,8 @@
                          wpa_cipher=hostapd_constants.WPA2_DEFAULT_CIPER,
                          wpa2_cipher=hostapd_constants.WPA2_DEFAULT_CIPER)
 
+SECURITIES = [None, WPA2_SECURITY]
+
 
 def generate_test_name(settings):
     """Generates a test name string based on the ac_capabilities for
@@ -108,7 +107,7 @@
 
 
 # 6912 test cases
-class WlanPhyCompliance11ACTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanPhyCompliance11ACTest(WifiBaseTest):
     """Tests for validating 11ac PHYS.
 
     Test Bed Requirement:
@@ -117,17 +116,15 @@
     """
 
     def __init__(self, controllers):
-        WifiBaseTest.__init__(self, controllers)
-        self.tests = [
-            'test_11ac_capabilities_20mhz_open',
-            'test_11ac_capabilities_40mhz_open',
-            'test_11ac_capabilities_80mhz_open',
-            'test_11ac_capabilities_20mhz_wpa2',
-            'test_11ac_capabilities_40mhz_wpa2',
-            'test_11ac_capabilities_80mhz_wpa2'
-        ]
-        if 'debug_11ac_tests' in self.user_params:
-            self.tests.append('test_11ac_capabilities_debug')
+        super().__init__(controllers)
+
+    def setup_generated_tests(self):
+        test_args = self._generate_20mhz_test_args() + \
+            self._generate_40mhz_test_args() + \
+            self._generate_80mhz_test_args()
+        self.generate_tests(test_logic=self.setup_and_connect,
+                            name_func=generate_test_name,
+                            arg_sets=test_args)
 
     def setup_class(self):
         super().setup_class()
@@ -202,133 +199,64 @@
                                target_security=target_security),
             'Failed to associate.')
 
-    # 864 test cases
-    def test_11ac_capabilities_20mhz_open(self):
-        test_list = []
-        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC, RX_STBC,
-                                             TX_STBC, MAX_A_MPDU, RX_ANTENNA,
-                                             TX_ANTENNA):
-            test_list.append({
-                'chbw': 20,
-                'security': None,
-                'n_capabilities': N_CAPABS_20MHZ,
-                'ac_capabilities': combination
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
+    # 1728 tests
+    def _generate_20mhz_test_args(self):
+        test_args = []
 
-    # 864 test cases
-    def test_11ac_capabilities_40mhz_open(self):
-        test_list = []
-        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC, RX_STBC,
-                                             TX_STBC, MAX_A_MPDU, RX_ANTENNA,
-                                             TX_ANTENNA):
-            test_list.append({
-                'chbw': 40,
-                'security': None,
-                'n_capabilities': N_CAPABS_40MHZ,
-                'ac_capabilities': combination
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    # 1728 test cases
-    def test_11ac_capabilities_80mhz_open(self):
-        test_list = []
-        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC,
-                                             SHORT_GI_80, RX_STBC, TX_STBC,
+        # 864 test cases for open security
+        # 864 test cases for wpa2 security
+        for combination in itertools.product(SECURITIES, VHT_MAX_MPDU_LEN,
+                                             RXLDPC, RX_STBC, TX_STBC,
                                              MAX_A_MPDU, RX_ANTENNA,
                                              TX_ANTENNA):
-            test_list.append({
-                'chbw': 80,
-                'security': None,
-                'n_capabilities': N_CAPABS_40MHZ,
-                'ac_capabilities': combination
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    # 864 test cases
-    def test_11ac_capabilities_20mhz_wpa2(self):
-        test_list = []
-        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC, RX_STBC,
-                                             TX_STBC, MAX_A_MPDU, RX_ANTENNA,
-                                             TX_ANTENNA):
-            test_list.append({
+            security = combination[0]
+            ac_capabilities = combination[1:]
+            test_args.append(({
                 'chbw': 20,
-                'security': WPA2_SECURITY,
-                'n_capabilities': N_CAPABS_20MHZ,
-                'ac_capabilities': combination
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    # 864 test cases
-    def test_11ac_capabilities_40mhz_wpa2(self):
-        test_list = []
-        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC, RX_STBC,
-                                             TX_STBC, MAX_A_MPDU, RX_ANTENNA,
-                                             TX_ANTENNA):
-            test_list.append({
-                'chbw': 40,
-                'security': WPA2_SECURITY,
-                'n_capabilities': N_CAPABS_40MHZ,
-                'ac_capabilities': combination
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    # 1728 test cases
-    def test_11ac_capabilities_80mhz_wpa2(self):
-        test_list = []
-        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC,
-                                             SHORT_GI_80, RX_STBC, TX_STBC,
-                                             MAX_A_MPDU, RX_ANTENNA,
-                                             TX_ANTENNA):
-            test_list.append({
-                'chbw': 80,
-                'security': WPA2_SECURITY,
-                'n_capabilities': N_CAPABS_40MHZ,
-                'ac_capabilities': combination
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    def test_11ac_capabilities_debug(self):
-        chbw_sec_capabs = re.compile(
-            r'.*?([0-9]*)mhz.*?(open|wpa2).*?(\[.*\])', re.IGNORECASE)
-        test_list = []
-        for test_name in self.user_params['debug_11ac_tests']:
-            test_to_run = re.match(chbw_sec_capabs, test_name)
-            chbw = int(test_to_run.group(1))
-            security = test_to_run.group(2)
-            capabs = test_to_run.group(3)
-            if chbw >= 40:
-                n_capabs = N_CAPABS_40MHZ
-            else:
-                n_capabs = N_CAPABS_20MHZ
-            if security.lower() == 'open':
-                security = None
-            elif security.lower() == 'wpa2':
-                security = WPA2_SECURITY
-            if capabs:
-                ac_capabs_strings = re.findall(r'\[.*?\]', capabs)
-                ac_capabs = [
-                    hostapd_constants.AC_CAPABILITIES_MAPPING_INVERSE[k]
-                    for k in ac_capabs_strings
-                ]
-            test_list.append({
-                'chbw': chbw,
                 'security': security,
-                'n_capabilities': n_capabs,
-                'ac_capabilities': ac_capabs
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
+                'n_capabilities': N_CAPABS_20MHZ,
+                'ac_capabilities': ac_capabilities
+            }, ))
+
+        return test_args
+
+    # 1728 tests
+    def _generate_40mhz_test_args(self):
+        test_args = []
+
+        # 864 test cases for open security
+        # 864 test cases for wpa2 security
+        for combination in itertools.product(SECURITIES, VHT_MAX_MPDU_LEN,
+                                             RXLDPC, RX_STBC, TX_STBC,
+                                             MAX_A_MPDU, RX_ANTENNA,
+                                             TX_ANTENNA):
+            security = combination[0]
+            ac_capabilities = combination[1:]
+            test_args.append(({
+                'chbw': 40,
+                'security': security,
+                'n_capabilities': N_CAPABS_40MHZ,
+                'ac_capabilities': ac_capabilities
+            }, ))
+
+        return test_args
+
+    # 3456 tests
+    def _generate_80mhz_test_args(self):
+        test_args = []
+
+        # 1728 test cases for open security
+        # 1728 test cases for wpa2 security
+        for combination in itertools.product(SECURITIES, VHT_MAX_MPDU_LEN,
+                                             RXLDPC, SHORT_GI_80, RX_STBC,
+                                             TX_STBC, MAX_A_MPDU, RX_ANTENNA,
+                                             TX_ANTENNA):
+            security = combination[0]
+            ac_capabilities = combination[1:]
+            test_args.append(({
+                'chbw': 80,
+                'security': security,
+                'n_capabilities': N_CAPABS_40MHZ,
+                'ac_capabilities': ac_capabilities
+            }, ))
+        return test_args
diff --git a/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyCompliance11NTest.py b/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyCompliance11NTest.py
index ad0800f..bf59806 100644
--- a/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyCompliance11NTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyCompliance11NTest.py
@@ -15,7 +15,6 @@
 #   limitations under the License.
 
 import itertools
-import re
 
 from acts import asserts
 from acts import utils
@@ -25,7 +24,6 @@
 from acts.controllers.ap_lib.hostapd_security import Security
 from acts.controllers.ap_lib.hostapd_utils import generate_random_password
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 
 FREQUENCY_24 = ['2.4GHz']
@@ -35,6 +33,7 @@
 CHANNEL_BANDWIDTH_40_UPPER = ['HT40+']
 SECURITY_OPEN = 'open'
 SECURITY_WPA2 = 'wpa2'
+N_MODE = [hostapd_constants.MODE_11N_PURE, hostapd_constants.MODE_11N_MIXED]
 LDPC = [hostapd_constants.N_CAPABILITY_LDPC, '']
 TX_STBC = [hostapd_constants.N_CAPABILITY_TX_STBC, '']
 RX_STBC = [hostapd_constants.N_CAPABILITY_RX_STBC1, '']
@@ -59,11 +58,19 @@
     for cap in hostapd_constants.N_CAPABILITIES_MAPPING.keys():
         if cap in settings['n_capabilities']:
             ret.append(hostapd_constants.N_CAPABILITIES_MAPPING[cap])
-    return '%s_%s_%s_%s' % (settings['frequency'], settings['chbw'],
-                            settings['security'], ''.join(ret))
+    # '+' is used by Mobile Harness as special character, don't use it in test names
+    if settings['chbw'] == 'HT40-':
+        chbw = "HT40Lower"
+    elif settings['chbw'] == 'HT40+':
+        chbw = "HT40Upper"
+    else:
+        chbw = settings['chbw']
+    return 'test_11n_%s_%s_%s_%s_%s' % (settings['frequency'], chbw,
+                                        settings['security'],
+                                        settings['n_mode'], ''.join(ret))
 
 
-class WlanPhyCompliance11NTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanPhyCompliance11NTest(WifiBaseTest):
     """Tests for validating 11n PHYS.
 
     Test Bed Requirement:
@@ -72,23 +79,25 @@
     """
 
     def __init__(self, controllers):
-        WifiBaseTest.__init__(self, controllers)
-        self.tests = [
-            'test_11n_capabilities_24_HT20',
-            'test_11n_capabilities_24_HT40_lower',
-            'test_11n_capabilities_24_HT40_upper',
-            'test_11n_capabilities_5_HT20',
-            'test_11n_capabilities_5_HT40_lower',
-            'test_11n_capabilities_5_HT40_upper',
-            'test_11n_capabilities_24_HT20_wpa2',
-            'test_11n_capabilities_24_HT40_lower_wpa2',
-            'test_11n_capabilities_24_HT40_upper_wpa2',
-            'test_11n_capabilities_5_HT20_wpa2',
-            'test_11n_capabilities_5_HT40_lower_wpa2',
-            'test_11n_capabilities_5_HT40_upper_wpa2'
-        ]
-        if 'debug_11n_tests' in self.user_params:
-            self.tests.append('test_11n_capabilities_debug')
+        super().__init__(controllers)
+
+    def setup_generated_tests(self):
+        test_args = self._generate_24_HT20_test_args() + \
+            self._generate_24_HT40_lower_test_args() + \
+            self._generate_24_HT40_upper_test_args() + \
+            self._generate_5_HT20_test_args() + \
+            self._generate_5_HT40_lower_test_args() + \
+            self._generate_5_HT40_upper_test_args() + \
+            self._generate_24_HT20_wpa2_test_args() + \
+            self._generate_24_HT40_lower_wpa2_test_args() + \
+            self._generate_24_HT40_upper_wpa2_test_args() + \
+            self._generate_5_HT20_wpa2_test_args() + \
+            self._generate_5_HT40_lower_wpa2_test_args() + \
+            self._generate_5_HT40_upper_wpa2_test_args()
+
+        self.generate_tests(test_logic=self.setup_and_connect,
+                            name_func=generate_test_name,
+                            arg_sets=test_args)
 
     def setup_class(self):
         super().setup_class()
@@ -185,9 +194,14 @@
             password = security_profile.password
         target_security = hostapd_constants.SECURITY_STRING_TO_DEFAULT_TARGET_SECURITY.get(
             ap_settings['security'], None)
+
+        mode = ap_settings['n_mode']
+        if mode not in N_MODE:
+            raise ValueError('Invalid n-mode: %s' % ap_settings['n-mode'])
+
         setup_ap(access_point=self.access_point,
                  profile_name='whirlwind',
-                 mode=hostapd_constants.MODE_11N_MIXED,
+                 mode=mode,
                  channel=channel,
                  n_capabilities=n_capabilities,
                  ac_capabilities=[],
@@ -201,8 +215,120 @@
                                target_security=target_security),
             'Failed to connect.')
 
-    def test_11n_capabilities_24_HT20(self):
-        test_list = []
+    def _generate_24_HT20_test_args(self):
+        test_args = []
+        for combination in itertools.product(FREQUENCY_24,
+                                             CHANNEL_BANDWIDTH_20, N_MODE,
+                                             LDPC, TX_STBC, RX_STBC, SGI_20,
+                                             INTOLERANT_40, MAX_AMPDU_7935,
+                                             SMPS):
+            test_frequency = combination[0]
+            test_chbw = combination[1]
+            n_mode = combination[2]
+            n_capabilities = combination[3:]
+            test_args.append(({
+                'frequency': test_frequency,
+                'chbw': test_chbw,
+                'n_mode': n_mode,
+                'security': SECURITY_OPEN,
+                'n_capabilities': n_capabilities,
+            }, ))
+        return test_args
+
+    def _generate_24_HT40_lower_test_args(self):
+        test_args = []
+        for combination in itertools.product(FREQUENCY_24,
+                                             CHANNEL_BANDWIDTH_40_LOWER, LDPC,
+                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
+                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
+            test_frequency = combination[0]
+            test_chbw = combination[1]
+            n_capabilities = combination[2:]
+            test_args.append(({
+                'frequency': test_frequency,
+                'chbw': test_chbw,
+                'n_mode': hostapd_constants.MODE_11N_MIXED,
+                'security': SECURITY_OPEN,
+                'n_capabilities': n_capabilities
+            }, ))
+        return test_args
+
+    def _generate_24_HT40_upper_test_args(self):
+        test_args = []
+        for combination in itertools.product(FREQUENCY_24,
+                                             CHANNEL_BANDWIDTH_40_UPPER, LDPC,
+                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
+                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
+            test_frequency = combination[0]
+            test_chbw = combination[1]
+            n_capabilities = combination[2:]
+            test_args.append(({
+                'frequency': test_frequency,
+                'chbw': test_chbw,
+                'n_mode': hostapd_constants.MODE_11N_MIXED,
+                'security': SECURITY_OPEN,
+                'n_capabilities': n_capabilities
+            }, ))
+        return test_args
+
+    def _generate_5_HT20_test_args(self):
+        test_args = []
+        for combination in itertools.product(FREQUENCY_5, CHANNEL_BANDWIDTH_20,
+                                             LDPC, TX_STBC, RX_STBC, SGI_20,
+                                             INTOLERANT_40, MAX_AMPDU_7935,
+                                             SMPS):
+            test_frequency = combination[0]
+            test_chbw = combination[1]
+            n_capabilities = combination[2:]
+            test_args.append(({
+                'frequency': test_frequency,
+                'chbw': test_chbw,
+                'n_mode': hostapd_constants.MODE_11N_MIXED,
+                'security': SECURITY_OPEN,
+                'n_capabilities': n_capabilities
+            }, ))
+        return test_args
+
+    def _generate_5_HT40_lower_test_args(self):
+        test_args = []
+        for combination in itertools.product(FREQUENCY_5,
+                                             CHANNEL_BANDWIDTH_40_LOWER, LDPC,
+                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
+                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
+            test_frequency = combination[0]
+            test_chbw = combination[1]
+            n_capabilities = combination[2:]
+            test_args.append(({
+                'frequency': test_frequency,
+                'chbw': test_chbw,
+                'n_mode': hostapd_constants.MODE_11N_MIXED,
+                'security': SECURITY_OPEN,
+                'n_capabilities': n_capabilities
+            }, ))
+        return test_args
+
+    def _generate_5_HT40_upper_test_args(self):
+        test_args = []
+        for combination in itertools.product(FREQUENCY_5,
+                                             CHANNEL_BANDWIDTH_40_UPPER,
+                                             N_MODE, LDPC, TX_STBC, RX_STBC,
+                                             SGI_20, SGI_40, MAX_AMPDU_7935,
+                                             SMPS, DSSS_CCK):
+            test_frequency = combination[0]
+            test_chbw = combination[1]
+            n_mode = combination[2]
+            n_capabilities = combination[3:]
+            test_args.append(({
+                'frequency': test_frequency,
+                'chbw': test_chbw,
+                'n_mode': n_mode,
+                'security': SECURITY_OPEN,
+                'n_capabilities': n_capabilities
+            }, ))
+        return test_args
+
+    def _generate_24_HT20_wpa2_test_args(self):
+        test_args = []
         for combination in itertools.product(FREQUENCY_24,
                                              CHANNEL_BANDWIDTH_20, LDPC,
                                              TX_STBC, RX_STBC, SGI_20,
@@ -211,18 +337,17 @@
             test_frequency = combination[0]
             test_chbw = combination[1]
             n_capabilities = combination[2:]
-            test_list.append({
+            test_args.append(({
                 'frequency': test_frequency,
                 'chbw': test_chbw,
-                'security': SECURITY_OPEN,
+                'n_mode': hostapd_constants.MODE_11N_MIXED,
+                'security': SECURITY_WPA2,
                 'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
+            }, ))
+        return test_args
 
-    def test_11n_capabilities_24_HT40_lower(self):
-        test_list = []
+    def _generate_24_HT40_lower_wpa2_test_args(self):
+        test_args = []
         for combination in itertools.product(FREQUENCY_24,
                                              CHANNEL_BANDWIDTH_40_LOWER, LDPC,
                                              TX_STBC, RX_STBC, SGI_20, SGI_40,
@@ -230,18 +355,17 @@
             test_frequency = combination[0]
             test_chbw = combination[1]
             n_capabilities = combination[2:]
-            test_list.append({
+            test_args.append(({
                 'frequency': test_frequency,
                 'chbw': test_chbw,
-                'security': SECURITY_OPEN,
+                'n_mode': hostapd_constants.MODE_11N_MIXED,
+                'security': SECURITY_WPA2,
                 'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
+            }, ))
+        return test_args
 
-    def test_11n_capabilities_24_HT40_upper(self):
-        test_list = []
+    def _generate_24_HT40_upper_wpa2_test_args(self):
+        test_args = []
         for combination in itertools.product(FREQUENCY_24,
                                              CHANNEL_BANDWIDTH_40_UPPER, LDPC,
                                              TX_STBC, RX_STBC, SGI_20, SGI_40,
@@ -249,18 +373,17 @@
             test_frequency = combination[0]
             test_chbw = combination[1]
             n_capabilities = combination[2:]
-            test_list.append({
+            test_args.append(({
                 'frequency': test_frequency,
                 'chbw': test_chbw,
-                'security': SECURITY_OPEN,
+                'n_mode': hostapd_constants.MODE_11N_MIXED,
+                'security': SECURITY_WPA2,
                 'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
+            }, ))
+        return test_args
 
-    def test_11n_capabilities_5_HT20(self):
-        test_list = []
+    def _generate_5_HT20_wpa2_test_args(self):
+        test_args = []
         for combination in itertools.product(FREQUENCY_5, CHANNEL_BANDWIDTH_20,
                                              LDPC, TX_STBC, RX_STBC, SGI_20,
                                              INTOLERANT_40, MAX_AMPDU_7935,
@@ -268,18 +391,17 @@
             test_frequency = combination[0]
             test_chbw = combination[1]
             n_capabilities = combination[2:]
-            test_list.append({
+            test_args.append(({
                 'frequency': test_frequency,
                 'chbw': test_chbw,
-                'security': SECURITY_OPEN,
+                'n_mode': hostapd_constants.MODE_11N_MIXED,
+                'security': SECURITY_WPA2,
                 'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
+            }, ))
+        return test_args
 
-    def test_11n_capabilities_5_HT40_lower(self):
-        test_list = []
+    def _generate_5_HT40_lower_wpa2_test_args(self):
+        test_args = []
         for combination in itertools.product(FREQUENCY_5,
                                              CHANNEL_BANDWIDTH_40_LOWER, LDPC,
                                              TX_STBC, RX_STBC, SGI_20, SGI_40,
@@ -287,18 +409,17 @@
             test_frequency = combination[0]
             test_chbw = combination[1]
             n_capabilities = combination[2:]
-            test_list.append({
+            test_args.append(({
                 'frequency': test_frequency,
                 'chbw': test_chbw,
-                'security': SECURITY_OPEN,
+                'n_mode': hostapd_constants.MODE_11N_MIXED,
+                'security': SECURITY_WPA2,
                 'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
+            }, ))
+        return test_args
 
-    def test_11n_capabilities_5_HT40_upper(self):
-        test_list = []
+    def _generate_5_HT40_upper_wpa2_test_args(self):
+        test_args = []
         for combination in itertools.product(FREQUENCY_5,
                                              CHANNEL_BANDWIDTH_40_UPPER, LDPC,
                                              TX_STBC, RX_STBC, SGI_20, SGI_40,
@@ -306,192 +427,11 @@
             test_frequency = combination[0]
             test_chbw = combination[1]
             n_capabilities = combination[2:]
-            test_list.append({
+            test_args.append(({
                 'frequency': test_frequency,
                 'chbw': test_chbw,
-                'security': SECURITY_OPEN,
-                'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    def test_11n_capabilities_24_HT20_wpa2(self):
-        test_list = []
-        for combination in itertools.product(FREQUENCY_24,
-                                             CHANNEL_BANDWIDTH_20, LDPC,
-                                             TX_STBC, RX_STBC, SGI_20,
-                                             INTOLERANT_40, MAX_AMPDU_7935,
-                                             SMPS):
-            test_frequency = combination[0]
-            test_chbw = combination[1]
-            n_capabilities = combination[2:]
-            test_list.append({
-                'frequency': test_frequency,
-                'chbw': test_chbw,
+                'n_mode': hostapd_constants.MODE_11N_MIXED,
                 'security': SECURITY_WPA2,
                 'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    def test_11n_capabilities_24_HT40_lower_wpa2(self):
-        test_list = []
-        for combination in itertools.product(FREQUENCY_24,
-                                             CHANNEL_BANDWIDTH_40_LOWER, LDPC,
-                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
-                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
-            test_frequency = combination[0]
-            test_chbw = combination[1]
-            n_capabilities = combination[2:]
-            test_list.append({
-                'frequency': test_frequency,
-                'chbw': test_chbw,
-                'security': SECURITY_WPA2,
-                'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    def test_11n_capabilities_24_HT40_upper_wpa2(self):
-        test_list = []
-        for combination in itertools.product(FREQUENCY_24,
-                                             CHANNEL_BANDWIDTH_40_UPPER, LDPC,
-                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
-                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
-            test_frequency = combination[0]
-            test_chbw = combination[1]
-            n_capabilities = combination[2:]
-            test_list.append({
-                'frequency': test_frequency,
-                'chbw': test_chbw,
-                'security': SECURITY_WPA2,
-                'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    def test_11n_capabilities_5_HT20_wpa2(self):
-        test_list = []
-        for combination in itertools.product(FREQUENCY_5, CHANNEL_BANDWIDTH_20,
-                                             LDPC, TX_STBC, RX_STBC, SGI_20,
-                                             INTOLERANT_40, MAX_AMPDU_7935,
-                                             SMPS):
-            test_frequency = combination[0]
-            test_chbw = combination[1]
-            n_capabilities = combination[2:]
-            test_list.append({
-                'frequency': test_frequency,
-                'chbw': test_chbw,
-                'security': SECURITY_WPA2,
-                'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    def test_11n_capabilities_5_HT40_lower_wpa2(self):
-        test_list = []
-        for combination in itertools.product(FREQUENCY_5,
-                                             CHANNEL_BANDWIDTH_40_LOWER, LDPC,
-                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
-                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
-            test_frequency = combination[0]
-            test_chbw = combination[1]
-            n_capabilities = combination[2:]
-            test_list.append({
-                'frequency': test_frequency,
-                'chbw': test_chbw,
-                'security': SECURITY_WPA2,
-                'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    def test_11n_capabilities_5_HT40_upper_wpa2(self):
-        test_list = []
-        for combination in itertools.product(FREQUENCY_5,
-                                             CHANNEL_BANDWIDTH_40_UPPER, LDPC,
-                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
-                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
-            test_frequency = combination[0]
-            test_chbw = combination[1]
-            n_capabilities = combination[2:]
-            test_list.append({
-                'frequency': test_frequency,
-                'chbw': test_chbw,
-                'security': SECURITY_WPA2,
-                'n_capabilities': n_capabilities
-            })
-        self.run_generated_testcases(self.setup_and_connect,
-                                     settings=test_list,
-                                     name_func=generate_test_name)
-
-    def test_11n_capabilities_debug(self):
-        allowed_frequencies = FREQUENCY_5 + FREQUENCY_24
-        allowed_chbw = (CHANNEL_BANDWIDTH_20 + CHANNEL_BANDWIDTH_40_LOWER +
-                        CHANNEL_BANDWIDTH_40_UPPER)
-        allowed_security = [SECURITY_WPA2, SECURITY_OPEN]
-        freq_chbw_sec = re.compile(r'(.*)_(.*)_(.*)_(\[.*\])?$')
-        for test_title in self.user_params['debug_11n_tests']:
-            test_list = []
-            test_to_run = re.match(freq_chbw_sec, test_title)
-            if test_to_run:
-                test_frequency = test_to_run.group(1)
-                test_chbw = test_to_run.group(2)
-                security = test_to_run.group(3)
-                if (test_frequency in allowed_frequencies
-                        and test_chbw in allowed_chbw
-                        and security in allowed_security):
-                    if test_to_run.group(4):
-                        n_capabilities_str = test_to_run.group(4)
-                    else:
-                        n_capabilities_str = ''
-                    n_capabilities_list = []
-                    if '[LDPC]' in n_capabilities_str:
-                        n_capabilities_list.append(
-                            hostapd_constants.N_CAPABILITY_LDPC)
-                    if '[TX-STBC]' in n_capabilities_str:
-                        n_capabilities_list.append(
-                            hostapd_constants.N_CAPABILITY_TX_STBC)
-                    if '[RX-STBC1]' in n_capabilities_str:
-                        n_capabilities_list.append(
-                            hostapd_constants.N_CAPABILITY_RX_STBC1)
-                    if '[SHORT-GI-20]' in n_capabilities_str:
-                        n_capabilities_list.append(
-                            hostapd_constants.N_CAPABILITY_SGI20)
-                    if '[SHORT-GI-40]' in n_capabilities_str:
-                        n_capabilities_list.append(
-                            hostapd_constants.N_CAPABILITY_SGI40)
-                    if '[DSSS_CCK-40]' in n_capabilities_str:
-                        n_capabilities_list.append(
-                            hostapd_constants.N_CAPABILITY_DSSS_CCK_40)
-                    if '[40-INTOLERANT]' in n_capabilities_str:
-                        n_capabilities_list.append(
-                            hostapd_constants.N_CAPABILITY_40_INTOLERANT)
-                    if '[MAX-AMSDU-7935]' in n_capabilities_str:
-                        n_capabilities_list.append(
-                            hostapd_constants.N_CAPABILITY_MAX_AMSDU_7935)
-                    if '[SMPS-STATIC]' in n_capabilities_str:
-                        n_capabilities_list.append(
-                            hostapd_constants.N_CAPABILITY_SMPS_STATIC)
-                    n_capabilities = tuple(n_capabilities_list)
-                    test_list.append({
-                        'frequency': test_frequency,
-                        'chbw': test_chbw,
-                        'security': security,
-                        'n_capabilities': n_capabilities
-                    })
-                    self.run_generated_testcases(self.setup_and_connect,
-                                                 settings=test_list,
-                                                 name_func=generate_test_name)
-                else:
-                    self.log.error('Invalid test (%s). Trying the next one.' %
-                                   test_title)
-            else:
-                self.log.error('Invalid test (%s). Trying the next one.' %
-                               test_title)
+            }, ))
+        return test_args
diff --git a/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyComplianceABGTest.py b/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyComplianceABGTest.py
index 2d17f4b..fb4099b 100644
--- a/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyComplianceABGTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/compliance/WlanPhyComplianceABGTest.py
@@ -18,14 +18,12 @@
 from acts import utils
 
 from acts.controllers.access_point import setup_ap
-from acts.controllers.ap_lib import hostapd_ap_preset
 from acts.controllers.ap_lib import hostapd_constants
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 
 
-class WlanPhyComplianceABGTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanPhyComplianceABGTest(WifiBaseTest):
     """Tests for validating 11a, 11b, and 11g PHYS.
 
     Test Bed Requirement:
diff --git a/acts_tests/tests/google/fuchsia/wlan/compliance/WlanSecurityComplianceABGTest.py b/acts_tests/tests/google/fuchsia/wlan/compliance/WlanSecurityComplianceABGTest.py
index a32c7c4..971fe8b 100644
--- a/acts_tests/tests/google/fuchsia/wlan/compliance/WlanSecurityComplianceABGTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/compliance/WlanSecurityComplianceABGTest.py
@@ -24,7 +24,7 @@
 from acts.controllers.ap_lib.hostapd_security import Security
 from acts.controllers.ap_lib.hostapd_utils import generate_random_password
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 
 AP_11ABG_PROFILE_NAME = 'whirlwind_11ag_legacy'
 SSID_LENGTH_DEFAULT = 15
@@ -156,7 +156,7 @@
     return security_profile_generator
 
 
-class WlanSecurityComplianceABGTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanSecurityComplianceABGTest(WifiBaseTest):
     """Tests for validating 11a, 11b, and 11g PHYS.
 
     Test Bed Requirement:
diff --git a/acts_tests/tests/google/fuchsia/wlan/facade/WlanDeprecatedConfigurationTest.py b/acts_tests/tests/google/fuchsia/wlan/facade/WlanDeprecatedConfigurationTest.py
index 44e6731..44eea1d 100644
--- a/acts_tests/tests/google/fuchsia/wlan/facade/WlanDeprecatedConfigurationTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/facade/WlanDeprecatedConfigurationTest.py
@@ -16,7 +16,7 @@
 
 from acts import asserts
 from acts import utils
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
 
 AP_ROLE = 'Ap'
@@ -29,8 +29,9 @@
 TEST_MAC_ADDR_SECONDARY = 'bc:9a:78:56:34:12'
 
 
-class WlanDeprecatedConfigurationTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanDeprecatedConfigurationTest(WifiBaseTest):
     """Tests for WlanDeprecatedConfigurationFacade"""
+
     def setup_class(self):
         super().setup_class()
         self.dut = create_wlan_device(self.fuchsia_devices[0])
@@ -51,21 +52,27 @@
             ConnectionError, if SL4F calls fail
             AttributeError, if no interface has role 'Ap'
         """
-        wlan_ifaces = self.dut.device.wlan_lib.wlanGetIfaceIdList()
+        wlan_ifaces = self.dut.device.sl4f.wlan_lib.wlanGetIfaceIdList()
         if wlan_ifaces.get('error'):
             raise ConnectionError('Failed to get wlan interface IDs: %s' %
                                   wlan_ifaces['error'])
 
         for wlan_iface in wlan_ifaces['result']:
-            iface_info = self.dut.device.wlan_lib.wlanQueryInterface(
+            iface_info = self.dut.device.sl4f.wlan_lib.wlanQueryInterface(
                 wlan_iface)
             if iface_info.get('error'):
                 raise ConnectionError('Failed to query wlan iface: %s' %
                                       iface_info['error'])
 
             if iface_info['result']['role'] == AP_ROLE:
-                return utils.mac_address_list_to_str(
-                    iface_info['result']['mac_addr'])
+                if 'mac_addr' in iface_info['result']:
+                    return utils.mac_address_list_to_str(
+                            iface_info['result']['mac_addr'])
+                elif 'sta_addr' in iface_info['result']:
+                    return utils.mac_address_list_to_str(
+                            iface_info['result']['sta_addr'])
+                raise AttributeError(
+                    'AP iface info does not contain MAC address.')
         raise AttributeError(
             'Failed to get ap interface mac address. No AP interface found.')
 
@@ -77,7 +84,7 @@
         """
         self.log.info('Starting SoftAP on Fuchsia device (%s).' %
                       self.dut.device.ip)
-        response = self.dut.device.wlan_ap_policy_lib.wlanStartAccessPoint(
+        response = self.dut.device.sl4f.wlan_ap_policy_lib.wlanStartAccessPoint(
             DEFAULT_SSID, DEFAULT_SECURITY, DEFAULT_PASSWORD,
             DEFAULT_CONNECTIVITY_MODE, DEFAULT_OPERATING_BAND)
         if response.get('error'):
@@ -91,7 +98,8 @@
             ConnectionError, if SL4F call fails.
         """
         self.log.info('Stopping SoftAP.')
-        response = self.dut.device.wlan_ap_policy_lib.wlanStopAllAccessPoint()
+        response = self.dut.device.sl4f.wlan_ap_policy_lib.wlanStopAllAccessPoint(
+        )
         if response.get('error'):
             raise ConnectionError('Failed to stop SoftAP: %s' %
                                   response['error'])
@@ -107,7 +115,7 @@
         self.log.info(
             'Suggesting AP mac addr (%s) via wlan_deprecated_configuration_lib.'
             % mac_addr)
-        response = (self.dut.device.wlan_deprecated_configuration_lib.
+        response = (self.dut.device.sl4f.wlan_deprecated_configuration_lib.
                     wlanSuggestAccessPointMacAddress(mac_addr))
         if response.get('error'):
             asserts.fail('Failed to suggest AP mac address (%s): %s' %
diff --git a/acts_tests/tests/google/fuchsia/wlan/facade/WlanFacadeTest.py b/acts_tests/tests/google/fuchsia/wlan/facade/WlanFacadeTest.py
index 9f884b6..0f591e0 100644
--- a/acts_tests/tests/google/fuchsia/wlan/facade/WlanFacadeTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/facade/WlanFacadeTest.py
@@ -20,11 +20,12 @@
 import array
 
 from acts import asserts, signals
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
 
 
-class WlanFacadeTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanFacadeTest(WifiBaseTest):
+
     def setup_class(self):
         super().setup_class()
         if len(self.fuchsia_devices) < 1:
@@ -34,7 +35,7 @@
         self.dut = create_wlan_device(self.fuchsia_devices[0])
 
     def test_get_phy_id_list(self):
-        result = self.dut.device.wlan_lib.wlanPhyIdList()
+        result = self.dut.device.sl4f.wlan_lib.wlanPhyIdList()
         error = result['error']
         asserts.assert_true(error is None, error)
 
@@ -42,7 +43,7 @@
         return True
 
     def test_get_country(self):
-        wlan_lib = self.dut.device.wlan_lib
+        wlan_lib = self.dut.device.sl4f.wlan_lib
 
         result = wlan_lib.wlanPhyIdList()
         error = result['error']
@@ -60,7 +61,7 @@
         return True
 
     def test_get_dev_path(self):
-        wlan_lib = self.dut.device.wlan_lib
+        wlan_lib = self.dut.device.sl4f.wlan_lib
 
         result = wlan_lib.wlanPhyIdList()
         error = result['error']
diff --git a/acts_tests/tests/google/fuchsia/wlan/facade/WlanStatusTest.py b/acts_tests/tests/google/fuchsia/wlan/facade/WlanStatusTest.py
index 10344b2..239fe04 100644
--- a/acts_tests/tests/google/fuchsia/wlan/facade/WlanStatusTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/facade/WlanStatusTest.py
@@ -18,15 +18,16 @@
 """
 
 from acts import signals
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 
 
-class WlanStatusTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanStatusTest(WifiBaseTest):
     """WLAN status test class.
 
     Test Bed Requirements:
     * One or more Fuchsia devices with WLAN client capabilities.
     """
+
     def setup_class(self):
         super().setup_class()
         for fd in self.fuchsia_devices:
@@ -48,7 +49,7 @@
         for fd in self.fuchsia_devices:
             fd.deconfigure_wlan()
 
-            status = fd.wlan_lib.wlanStatus()
+            status = fd.sl4f.wlan_lib.wlanStatus()
             self.log.debug(status)
             if not status["error"] or status["result"]:
                 raise signals.TestFailure(
@@ -67,7 +68,7 @@
             fd.configure_wlan(association_mechanism='policy',
                               preserve_saved_networks=True)
 
-            status = fd.wlan_lib.wlanStatus()
+            status = fd.sl4f.wlan_lib.wlanStatus()
             self.log.debug(status)
             if status["error"] or not status["result"]:
                 raise signals.TestFailure(
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/BeaconLossTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/BeaconLossTest.py
index a2a763b..0f33dfa 100644
--- a/acts_tests/tests/google/fuchsia/wlan/functional/BeaconLossTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/BeaconLossTest.py
@@ -22,8 +22,6 @@
 "beacon_loss_test_iterations": "5"
 """
 
-import os
-import uuid
 import time
 
 from acts import asserts
@@ -32,12 +30,12 @@
 from acts.controllers.access_point import setup_ap
 from acts.controllers.ap_lib import hostapd_constants
 
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 from acts.utils import rand_ascii_str
 
 
-class BeaconLossTest(AbstractDeviceWlanDeviceBaseTest):
+class BeaconLossTest(WifiBaseTest):
     # Default number of test iterations here.
     # Override using parameter in config file.
     # Eg: "beacon_loss_test_iterations": "10"
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/ChannelSwitchTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/ChannelSwitchTest.py
index b60184f..da6f39b 100644
--- a/acts_tests/tests/google/fuchsia/wlan/functional/ChannelSwitchTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/ChannelSwitchTest.py
@@ -24,12 +24,12 @@
 from acts.controllers.access_point import setup_ap
 from acts.controllers.ap_lib import hostapd_constants
 from acts.utils import rand_ascii_str
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 from typing import Sequence
 
 
-class ChannelSwitchTest(AbstractDeviceWlanDeviceBaseTest):
+class ChannelSwitchTest(WifiBaseTest):
     # Time to wait between issuing channel switches
     WAIT_BETWEEN_CHANNEL_SWITCHES_S = 15
 
@@ -211,8 +211,8 @@
             test_with_soft_ap=True)
 
     # TODO(fxbug.dev/84777): This test fails.
-    def test_channel_switch_regression_global_operating_class_115(self
-                                                                  ) -> None:
+    def test_channel_switch_regression_global_operating_class_115(
+            self) -> None:
         """Channel switch into, through, and out of global op. class 115 channels.
 
         Global operating class 115 is described in IEEE 802.11-2016 Table E-4.
@@ -243,8 +243,8 @@
             test_with_soft_ap=True)
 
     # TODO(fxbug.dev/84777): This test fails.
-    def test_channel_switch_regression_global_operating_class_124(self
-                                                                  ) -> None:
+    def test_channel_switch_regression_global_operating_class_124(
+            self) -> None:
         """Switch into, through, and out of global op. class 124 channels.
 
         Global operating class 124 is described in IEEE 802.11-2016 Table E-4.
@@ -307,7 +307,7 @@
 
         self.log.info('Starting SoftAP on DUT')
 
-        response = self.dut.device.wlan_ap_policy_lib.wlanStartAccessPoint(
+        response = self.dut.device.sl4f.wlan_ap_policy_lib.wlanStartAccessPoint(
             ssid, security_type, password, connectivity_mode, operating_band)
         if response.get('error'):
             raise EnvironmentError('SL4F: Failed to setup SoftAP. Err: %s' %
@@ -320,7 +320,8 @@
         Raises:
             EnvironmentError: if SoftAP stop call fails
         """
-        response = self.dut.device.wlan_ap_policy_lib.wlanStopAllAccessPoint()
+        response = self.dut.device.sl4f.wlan_ap_policy_lib.wlanStopAllAccessPoint(
+        )
         if response.get('error'):
             raise EnvironmentError(
                 'SL4F: Failed to stop all SoftAPs. Err: %s' %
@@ -362,12 +363,12 @@
         """
         iface_ids = self.dut.get_wlan_interface_id_list()
         for iface_id in iface_ids:
-            query = self.dut.device.wlan_lib.wlanQueryInterface(iface_id)
+            query = self.dut.device.sl4f.wlan_lib.wlanQueryInterface(iface_id)
             if query['error']:
                 continue
             query_result = query['result']
             if type(query_result) is dict and query_result.get('role') == 'Ap':
-                status = self.dut.device.wlan_lib.wlanStatus(iface_id)
+                status = self.dut.device.sl4f.wlan_lib.wlanStatus(iface_id)
                 if status['error']:
                     continue
                 status_result = status['result']
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/ConnectionStressTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/ConnectionStressTest.py
index 64fc144..eb35ccb 100644
--- a/acts_tests/tests/google/fuchsia/wlan/functional/ConnectionStressTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/ConnectionStressTest.py
@@ -18,22 +18,18 @@
 
 """
 
-import os
-import uuid
 import time
 
 from acts import signals
 from acts.controllers.access_point import setup_ap
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib import hostapd_security
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
-from acts_contrib.test_utils.fuchsia import utils
-from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
 from acts.utils import rand_ascii_str
 
 
-class ConnectionStressTest(AbstractDeviceWlanDeviceBaseTest):
+class ConnectionStressTest(WifiBaseTest):
     # Default number of test iterations here.
     # Override using parameter in config file.
     # Eg: "connection_stress_test_iterations": "50"
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/DownloadStressTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/DownloadStressTest.py
index 5ec6290..021bd7a 100644
--- a/acts_tests/tests/google/fuchsia/wlan/functional/DownloadStressTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/DownloadStressTest.py
@@ -17,21 +17,18 @@
 Script for testing various download stress scenarios.
 
 """
-import os
 import threading
-import uuid
 
 from acts import signals
 from acts.controllers.access_point import setup_ap
 from acts.controllers.ap_lib import hostapd_constants
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 from acts_contrib.test_utils.fuchsia import utils
-from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
 from acts.utils import rand_ascii_str
 
 
-class DownloadStressTest(AbstractDeviceWlanDeviceBaseTest):
+class DownloadStressTest(WifiBaseTest):
     # Default number of test iterations here.
     # Override using parameter in config file.
     # Eg: "download_stress_test_iterations": "10"
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/PingStressTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/PingStressTest.py
index 5f8addc..6145929 100644
--- a/acts_tests/tests/google/fuchsia/wlan/functional/PingStressTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/PingStressTest.py
@@ -14,48 +14,134 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 """
-Script for exercising various ping scenarios
-
+PingStressTest exercises sending ICMP and ICMPv6 pings to a wireless access
+router and another device behind the AP. Note, this does not reach out to the
+internet. The DUT is only responsible for sending a routable packet; any
+communication past the first-hop is not the responsibility of the DUT.
 """
 
-import os
 import threading
-import uuid
+
+from collections import namedtuple
 
 from acts import signals
+from acts import utils
+
 from acts.controllers.access_point import setup_ap
 from acts.controllers.ap_lib import hostapd_constants
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
-from acts_contrib.test_utils.tel.tel_test_utils import setup_droid_properties
-from acts_contrib.test_utils.fuchsia import utils
 from acts.utils import rand_ascii_str
 
+LOOPBACK_IPV4 = '127.0.0.1'
+LOOPBACK_IPV6 = '::1'
+PING_RESULT_TIMEOUT_SEC = 60 * 5
 
-class PingStressTest(AbstractDeviceWlanDeviceBaseTest):
-    # Timeout for ping thread in seconds
-    ping_thread_timeout_s = 60 * 5
+Test = namedtuple(
+    typename='Args',
+    field_names=['name', 'dest_ip', 'count', 'interval', 'timeout', 'size'],
+    defaults=[3, 1000, 1000, 25])
 
-    # List to capture ping results
-    ping_threads_result = []
+Addrs = namedtuple(
+    typename='Addrs',
+    field_names=['gateway_ipv4', 'gateway_ipv6', 'remote_ipv4', 'remote_ipv6'])
 
-    # IP addresses used in pings
-    google_dns_1 = '8.8.8.8'
-    google_dns_2 = '8.8.4.4'
+
+class PingStressTest(WifiBaseTest):
+
+    def setup_generated_tests(self):
+        self.generate_tests(
+            self.send_ping, lambda test_name, *_: f'test_{test_name}', [
+                Test("loopback_ipv4", LOOPBACK_IPV4),
+                Test("loopback_ipv6", LOOPBACK_IPV6),
+                Test("gateway_ipv4", lambda addrs: addrs.gateway_ipv4),
+                Test("gateway_ipv6", lambda addrs: addrs.gateway_ipv6),
+                Test("remote_ipv4_small_packet",
+                     lambda addrs: addrs.remote_ipv4),
+                Test("remote_ipv6_small_packet",
+                     lambda addrs: addrs.remote_ipv6),
+                Test("remote_ipv4_small_packet_long",
+                     lambda addrs: addrs.remote_ipv4,
+                     count=50),
+                Test("remote_ipv6_small_packet_long",
+                     lambda addrs: addrs.remote_ipv6,
+                     count=50),
+                Test("remote_ipv4_medium_packet",
+                     lambda addrs: addrs.remote_ipv4,
+                     size=64),
+                Test("remote_ipv6_medium_packet",
+                     lambda addrs: addrs.remote_ipv6,
+                     size=64),
+                Test("remote_ipv4_medium_packet_long",
+                     lambda addrs: addrs.remote_ipv4,
+                     count=50,
+                     timeout=1500,
+                     size=64),
+                Test("remote_ipv6_medium_packet_long",
+                     lambda addrs: addrs.remote_ipv6,
+                     count=50,
+                     timeout=1500,
+                     size=64),
+                Test("remote_ipv4_large_packet",
+                     lambda addrs: addrs.remote_ipv4,
+                     size=500),
+                Test("remote_ipv6_large_packet",
+                     lambda addrs: addrs.remote_ipv6,
+                     size=500),
+                Test("remote_ipv4_large_packet_long",
+                     lambda addrs: addrs.remote_ipv4,
+                     count=50,
+                     timeout=5000,
+                     size=500),
+                Test("remote_ipv6_large_packet_long",
+                     lambda addrs: addrs.remote_ipv6,
+                     count=50,
+                     timeout=5000,
+                     size=500),
+            ])
 
     def setup_class(self):
         super().setup_class()
-
         self.ssid = rand_ascii_str(10)
         self.dut = create_wlan_device(self.fuchsia_devices[0])
         self.access_point = self.access_points[0]
+        self.iperf_server = self.iperf_servers[0]
         setup_ap(access_point=self.access_point,
                  profile_name='whirlwind',
                  channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
                  ssid=self.ssid,
-                 setup_bridge=True)
+                 setup_bridge=True,
+                 is_ipv6_enabled=True,
+                 is_nat_enabled=False)
+
+        ap_bridges = self.access_point.interfaces.get_bridge_interface()
+        if len(ap_bridges) != 1:
+            raise signals.TestAbortClass(
+                f'Expected one bridge interface on the AP, got {ap_bridges}')
+        self.ap_ipv4 = utils.get_addr(self.access_point.ssh, ap_bridges[0])
+        self.ap_ipv6 = utils.get_addr(self.access_point.ssh,
+                                      ap_bridges[0],
+                                      addr_type='ipv6_link_local')
+        self.log.info(
+            f"Gateway finished setup ({self.ap_ipv4} | {self.ap_ipv6})")
+
+        self.iperf_server.renew_test_interface_ip_address()
+        self.iperf_server_ipv4 = self.iperf_server.get_addr()
+        self.iperf_server_ipv6 = self.iperf_server.get_addr(
+            addr_type='ipv6_private_local')
+        self.log.info(
+            f"Remote finished setup ({self.iperf_server_ipv4} | {self.iperf_server_ipv6})"
+        )
+
         self.dut.associate(self.ssid)
 
+        # Wait till the DUT has valid IP addresses after connecting.
+        self.dut.device.wait_for_ipv4_addr(
+            self.dut.device.wlan_client_test_interface_name)
+        self.dut.device.wait_for_ipv6_addr(
+            self.dut.device.wlan_client_test_interface_name)
+        self.log.info("DUT has valid IP addresses on test network")
+
     def teardown_class(self):
         self.dut.disconnect()
         self.dut.reset_wifi()
@@ -63,104 +149,76 @@
         self.access_point.stop_all_aps()
 
     def send_ping(self,
-                  dest_ip,
+                  _,
+                  get_addr_fn,
                   count=3,
                   interval=1000,
                   timeout=1000,
                   size=25):
-        self.log.info('Attempting to ping %s...' % dest_ip)
+        dest_ip = get_addr_fn(
+            Addrs(
+                gateway_ipv4=self.ap_ipv4,
+                # IPv6 link-local addresses require specification of the
+                # outgoing interface as the scope ID when sending packets.
+                gateway_ipv6=
+                f'{self.ap_ipv6}%{self.dut.get_default_wlan_test_interface()}',
+                remote_ipv4=self.iperf_server_ipv4,
+                # IPv6 global addresses do not require scope IDs.
+                remote_ipv6=self.iperf_server_ipv6)) if callable(
+                    get_addr_fn) else get_addr_fn
+
+        self.log.info(f'Attempting to ping {dest_ip}...')
         ping_result = self.dut.can_ping(dest_ip, count, interval, timeout,
                                         size)
         if ping_result:
             self.log.info('Ping was successful.')
         else:
-            if '8.8' in dest_ip:
-                raise signals.TestFailure('Ping was unsuccessful. Consider '
-                                          'possibility of server failure.')
-            else:
-                raise signals.TestFailure('Ping was unsuccessful.')
-        return True
-
-    def ping_thread(self, dest_ip):
-        self.log.info('Attempting to ping %s...' % dest_ip)
-        ping_result = self.dut.can_ping(dest_ip, count=10, size=50)
-        if ping_result:
-            self.log.info('Success pinging: %s' % dest_ip)
-        else:
-            self.log.info('Failure pinging: %s' % dest_ip)
-
-        self.ping_threads_result.append(ping_result)
-
-    def test_simple_ping(self):
-        return self.send_ping(self.google_dns_1)
-
-    def test_ping_local(self):
-        return self.send_ping('127.0.0.1')
-
-    def test_ping_AP(self):
-        return self.send_ping(self.access_point.ssh_settings.hostname)
-
-    def test_ping_with_params(self):
-        return self.send_ping(self.google_dns_1,
-                              count=5,
-                              interval=800,
-                              size=50)
-
-    def test_long_ping(self):
-        return self.send_ping(self.google_dns_1, count=50)
-
-    def test_medium_packet_ping(self):
-        return self.send_ping(self.google_dns_1, size=64)
-
-    def test_medium_packet_long_ping(self):
-        return self.send_ping(self.google_dns_1,
-                              count=50,
-                              timeout=1500,
-                              size=64)
-
-    def test_large_packet_ping(self):
-        return self.send_ping(self.google_dns_1, size=500)
-
-    def test_large_packet_long_ping(self):
-        return self.send_ping(self.google_dns_1,
-                              count=50,
-                              timeout=5000,
-                              size=500)
+            raise signals.TestFailure('Ping was unsuccessful.')
 
     def test_simultaneous_pings(self):
         ping_urls = [
-            self.google_dns_1, self.google_dns_2, self.google_dns_1,
-            self.google_dns_2
+            self.iperf_server_ipv4,
+            self.ap_ipv4,
+            self.iperf_server_ipv6,
+            f'{self.ap_ipv6}%{self.dut.get_default_wlan_test_interface()}',
         ]
         ping_threads = []
+        ping_results = []
+
+        def ping_thread(self, dest_ip, ping_results):
+            self.log.info('Attempting to ping %s...' % dest_ip)
+            ping_result = self.dut.can_ping(dest_ip, count=10, size=50)
+            if ping_result:
+                self.log.info('Success pinging: %s' % dest_ip)
+            else:
+                self.log.info('Failure pinging: %s' % dest_ip)
+            ping_results.append(ping_result)
 
         try:
             # Start multiple ping at the same time
             for index, url in enumerate(ping_urls):
-                self.log.info('Create and start thread %d.' % index)
-                t = threading.Thread(target=self.ping_thread, args=(url, ))
+                t = threading.Thread(target=ping_thread,
+                                     args=(self, url, ping_results))
                 ping_threads.append(t)
                 t.start()
 
             # Wait for all threads to complete or timeout
             for t in ping_threads:
-                t.join(self.ping_thread_timeout_s)
+                t.join(PING_RESULT_TIMEOUT_SEC)
 
         finally:
             is_alive = False
 
             for index, t in enumerate(ping_threads):
-                if t.isAlive():
+                if t.is_alive():
                     t = None
                     is_alive = True
 
             if is_alive:
-                raise signals.TestFailure('Thread %d timedout' % index)
+                raise signals.TestFailure(
+                    f'Timed out while pinging {ping_urls[index]}')
 
-        for index in range(0, len(self.ping_threads_result)):
-            if not self.ping_threads_result[index]:
-                self.log.info("Ping failed for %d" % index)
-                raise signals.TestFailure('Thread %d failed to ping. '
-                                          'Consider possibility of server '
-                                          'failure' % index)
+        for index in range(0, len(ping_results)):
+            if not ping_results[index]:
+                raise signals.TestFailure(f'Failed to ping {ping_urls[index]}')
         return True
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/SoftApTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/SoftApTest.py
index ec74992..84f70a4 100644
--- a/acts_tests/tests/google/fuchsia/wlan/functional/SoftApTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/SoftApTest.py
@@ -27,8 +27,8 @@
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib import hostapd_security
 from acts.controllers.ap_lib.hostapd_utils import generate_random_password
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 
 CONNECTIVITY_MODE_LOCAL = 'local_only'
 CONNECTIVITY_MODE_UNRESTRICTED = 'unrestricted'
@@ -129,10 +129,9 @@
 
 class StressTestIterationFailure(Exception):
     """Used to differentiate a subtest failure from an actual exception"""
-    pass
 
 
-class SoftApTest(AbstractDeviceWlanDeviceBaseTest):
+class SoftApTest(WifiBaseTest):
     """Tests for Fuchsia SoftAP
 
     Testbed requirement:
@@ -145,6 +144,7 @@
         tests), a physical AP (whirlwind) is also required. Those tests will be
         skipped if physical AP is not present.
     """
+
     def setup_class(self):
         self.soft_ap_test_params = self.user_params.get(
             'soft_ap_test_params', {})
@@ -234,7 +234,7 @@
 
         self.log.info('Starting SoftAP on DUT with settings: %s' % settings)
 
-        response = self.dut.device.wlan_ap_policy_lib.wlanStartAccessPoint(
+        response = self.dut.device.sl4f.wlan_ap_policy_lib.wlanStartAccessPoint(
             ssid, security_type, password, connectivity_mode, operating_band)
         if response.get('error'):
             raise EnvironmentError('SL4F: Failed to setup SoftAP. Err: %s' %
@@ -256,7 +256,7 @@
         security_type = settings['security_type']
         password = settings.get('password', '')
 
-        response = self.dut.device.wlan_ap_policy_lib.wlanStopAccessPoint(
+        response = self.dut.device.sl4f.wlan_ap_policy_lib.wlanStopAccessPoint(
             ssid, security_type, password)
         if response.get('error'):
             raise EnvironmentError('SL4F: Failed to stop SoftAP. Err: %s' %
@@ -268,7 +268,8 @@
         Raises:
             EnvironmentError, if StopAllAps call fails.
         """
-        response = self.dut.device.wlan_ap_policy_lib.wlanStopAllAccessPoint()
+        response = self.dut.device.sl4f.wlan_ap_policy_lib.wlanStopAllAccessPoint(
+        )
         if response.get('error'):
             raise EnvironmentError(
                 'SL4F: Failed to stop all SoftAPs. Err: %s' %
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/WlanRebootTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/WlanRebootTest.py
index 8d365a7..cc29e25 100644
--- a/acts_tests/tests/google/fuchsia/wlan/functional/WlanRebootTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/WlanRebootTest.py
@@ -25,7 +25,6 @@
 from acts import utils
 from acts.controllers import iperf_client
 from acts.controllers import iperf_server
-from acts.controllers import pdu
 from acts.controllers.access_point import setup_ap
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib.hostapd_security import Security
@@ -33,28 +32,40 @@
 from acts.controllers.ap_lib.radvd import Radvd
 from acts.controllers.ap_lib import radvd_constants
 from acts.controllers.ap_lib.radvd_config import RadvdConfig
+from acts.controllers.fuchsia_device import IP_ADDRESS_TIMEOUT
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 
 # Constants, for readibility
 AP = 'ap'
 DUT = 'dut'
+DEVICES = [AP, DUT]
+
 SOFT = 'soft'
 HARD = 'hard'
+REBOOT_TYPES = [SOFT, HARD]
+
 BAND_2G = '2g'
 BAND_5G = '5g'
 BANDS = [BAND_2G, BAND_5G]
+
 IPV4 = 'ipv4'
 IPV6 = 'ipv6'
 DUAL_IPV4_IPV6 = {IPV4: True, IPV6: True}
 IPV4_ONLY = {IPV4: True, IPV6: False}
 IPV6_ONLY = {IPV4: False, IPV6: True}
+IP_VERSIONS = [IPV4_ONLY, IPV6_ONLY, DUAL_IPV4_IPV6]
+
 INTERRUPTS = [True, False]
+OPEN_ENCRYPTION_STRING = "open"
+SECURITY_MODES = [
+    OPEN_ENCRYPTION_STRING, hostapd_constants.WPA2_STRING,
+    hostapd_constants.WPA3_STRING
+]
+
 DEFAULT_IPERF_TIMEOUT = 30
 
 DUT_NETWORK_CONNECTION_TIMEOUT = 60
-DUT_IP_ADDRESS_TIMEOUT = 15
 
 # Constants for Custom Reboot Tests
 ALL = 'all'
@@ -76,32 +87,7 @@
 CUSTOM_TEST_INTERRUPTS = {'true': [True], 'false': [False], ALL: [True, False]}
 
 
-def create_custom_test_name(reboot_device, reboot_type, band, ip_version,
-                            interrupt, iterations):
-    """Creates custom base test name, from custom test parameters"""
-    return ('test_custom_reboot_device_%s_type_%s_band_%s_ip_version_%s%s_with'
-            '_%s_iterations_each' %
-            (reboot_device, reboot_type, band, ip_version,
-             '_interrupt' if interrupt else '', iterations))
-
-
-def create_custom_subtest_name(settings):
-    """Creates custom subtest name, from subtest parameters."""
-    ipv4_str = '_ipv4' if settings[IPV4] else ''
-    ipv6_str = '_ipv6' if settings[IPV6] else ''
-    return 'test_custom_%s_reboot_%s_band_%s%s%s%s%s_with_%s_iterations' % (
-        settings['reboot_type'], settings['reboot_device'], settings['band'],
-        ipv4_str, ipv6_str, '_interrupt' if settings['interrupt'] else '',
-        '_%s' % settings['security_mode'] if settings['security_mode'] else '',
-        settings['iterations'])
-
-
-def get_test_name(settings):
-    """Returns test_name from custom generated subtest settings."""
-    return settings['test_name']
-
-
-class WlanRebootTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanRebootTest(WifiBaseTest):
     """Tests wlan reconnects in different reboot scenarios.
 
     Testbed Requirement:
@@ -109,8 +95,15 @@
     * One Whirlwind Access Point (will also serve as iperf server)
     * One PduDevice
     """
+
     def __init__(self, controllers):
-        WifiBaseTest.__init__(self, controllers)
+        super().__init__(controllers)
+
+    def setup_generated_tests(self):
+        self._read_wlan_reboot_test_params()
+        self.generate_tests(test_logic=self.run_reboot_test,
+                            name_func=self.generate_test_name,
+                            arg_sets=self.generate_test_args())
 
     def setup_class(self):
         super().setup_class()
@@ -131,16 +124,6 @@
 
         self.access_point = self.access_points[0]
 
-        # Times (in seconds) to wait for DUT network connection and assigning an
-        # ip address to the wlan interface.
-        self.wlan_reboot_test_params = self.user_params.get(
-            'wlan_reboot_test_params', {})
-        self.dut_network_connection_timeout = self.wlan_reboot_test_params.get(
-            'dut_network_connection_timeout', DUT_NETWORK_CONNECTION_TIMEOUT)
-        self.dut_ip_address_timeout = self.wlan_reboot_test_params.get(
-            'dut_ip_address_timeout', DUT_IP_ADDRESS_TIMEOUT)
-        self.skip_iperf = self.wlan_reboot_test_params.get('skip_iperf', False)
-
         self.iperf_server_on_ap = None
         self.iperf_client_on_dut = None
         if not self.skip_iperf:
@@ -221,81 +204,17 @@
         if not ipv4:
             self.access_point.stop_dhcp()
         if ipv6:
-            radvd_config = RadvdConfig(
-                prefix='fd00::/64',
-                adv_send_advert=radvd_constants.ADV_SEND_ADVERT_ON,
-                adv_on_link=radvd_constants.ADV_ON_LINK_ON,
-                adv_autonomous=radvd_constants.ADV_AUTONOMOUS_ON)
-
             if band == BAND_2G:
                 self.router_adv_daemon = Radvd(self.access_point.ssh,
                                                self.access_point.wlan_2g)
             elif band == BAND_5G:
                 self.router_adv_daemon = Radvd(self.access_point.ssh,
                                                self.access_point.wlan_5g)
+            radvd_config = RadvdConfig()
             self.router_adv_daemon.start(radvd_config)
 
         self.log.info('Network (SSID: %s) is up.' % ssid)
 
-    def wait_until_dut_gets_ipv4_addr(self, interface):
-        """Checks if device has an ipv4 private address. Sleeps 1 second between
-        retries.
-
-        Args:
-            interface: string, name of interface from which to get ipv4 address.
-
-        Raises:
-            ConnectionError, if DUT does not have an ipv4 address after all
-            timeout.
-        """
-        self.log.info(
-            'Checking if DUT has received an ipv4 addr. Will retry for %s '
-            'seconds.' % self.dut_ip_address_timeout)
-        timeout = time.time() + self.dut_ip_address_timeout
-        while time.time() < timeout:
-            ip_addrs = self.dut.get_interface_ip_addresses(interface)
-
-            if len(ip_addrs['ipv4_private']) > 0:
-                self.log.info('DUT has an ipv4 address: %s' %
-                              ip_addrs['ipv4_private'][0])
-                break
-            else:
-                self.log.debug(
-                    'DUT does not yet have an ipv4 address...retrying in 1 '
-                    'second.')
-                time.sleep(1)
-        else:
-            raise ConnectionError('DUT failed to get an ipv4 address.')
-
-    def wait_until_dut_gets_ipv6_addr(self, interface):
-        """Checks if device has an ipv6 private local address. Sleeps 1 second
-        between retries.
-
-        Args:
-            interface: string, name of interface from which to get ipv6 address.
-
-        Raises:
-            ConnectionError, if DUT does not have an ipv6 address after all
-            timeout.
-        """
-        self.log.info(
-            'Checking if DUT has received an ipv6 addr. Will retry for %s '
-            'seconds.' % self.dut_ip_address_timeout)
-        timeout = time.time() + self.dut_ip_address_timeout
-        while time.time() < timeout:
-            ip_addrs = self.dut.get_interface_ip_addresses(interface)
-            if len(ip_addrs['ipv6_private_local']) > 0:
-                self.log.info('DUT has an ipv6 private local address: %s' %
-                              ip_addrs['ipv6_private_local'][0])
-                break
-            else:
-                self.log.debug(
-                    'DUT does not yet have an ipv6 address...retrying in 1 '
-                    'second.')
-                time.sleep(1)
-        else:
-            raise ConnectionError('DUT failed to get an ipv6 address.')
-
     def setup_iperf_server_on_ap(self, band):
         """Configures iperf server based on the tests band.
 
@@ -359,7 +278,7 @@
             ConnectionError, if traffic is not passed successfully in both
                 directions.
         """
-        dut_ip_addresses = self.dut.get_interface_ip_addresses(
+        dut_ip_addresses = self.dut.device.get_interface_ip_addresses(
             iperf_client_on_dut.test_interface)
 
         iperf_server_ip_address = self.get_iperf_server_address(
@@ -597,9 +516,9 @@
         if not self.skip_iperf:
             dut_test_interface = self.iperf_client_on_dut.test_interface
             if ipv4:
-                self.wait_until_dut_gets_ipv4_addr(dut_test_interface)
+                self.dut.device.wait_for_ipv4_addr(dut_test_interface)
             if ipv6:
-                self.wait_until_dut_gets_ipv6_addr(dut_test_interface)
+                self.dut.device.wait_for_ipv6_addr(dut_test_interface)
 
             self.iperf_server_on_ap = self.setup_iperf_server_on_ap(band)
             self.iperf_server_on_ap.start()
@@ -657,9 +576,9 @@
 
                 if not self.skip_iperf:
                     if ipv4:
-                        self.wait_until_dut_gets_ipv4_addr(dut_test_interface)
+                        self.dut.device.wait_for_ipv4_addr(dut_test_interface)
                     if ipv6:
-                        self.wait_until_dut_gets_ipv6_addr(dut_test_interface)
+                        self.dut.device.wait_for_ipv6_addr(dut_test_interface)
 
                     self.iperf_server_on_ap.start()
 
@@ -689,882 +608,63 @@
                 'network %s %s/%s times.' %
                 (self.ssid, passed_count, iterations))
 
-# Open Network DUT Soft Reboots (requires a DUT and Whirlwind)
+    def generate_test_name(self, settings):
+        """Generates a test case name based on the reboot settings passed.
 
-    def test_soft_reboot_dut_ipv4_2g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
+        Args:
+            settings: A dictionary of settings related to reboot test.
 
-    def test_soft_reboot_dut_ipv4_5g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
+        Returns:
+            A string that represents a test case name.
+        """
+        test_name = "test_{reboot_type}_reboot_{reboot_device}_{band}_{security_mode}".format(
+            **settings)
 
-    def test_soft_reboot_dut_ipv6_2g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
+        if settings.get(IPV4):
+            test_name += "_ipv4"
 
-    def test_soft_reboot_dut_ipv6_5g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
+        if settings.get(IPV6):
+            test_name += "_ipv6"
 
-    def test_soft_reboot_dut_ipv4_ipv6_2g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
+        if settings.get('interrupt'):
+            test_name += "_interrupt"
 
-    def test_soft_reboot_dut_ipv4_ipv6_5g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
+        if settings.get('iterations'):
+            test_name += f"_with_{settings['iterations']}_iterations"
 
-# Open Network DUT Hard Reboots (requires a DUT, Whirlwind, and PDU)
+        return test_name
 
-    def test_hard_reboot_dut_ipv4_2g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
+    def generate_test_args(self):
+        # If custom reboot tests present in ACTS config then run just those
+        test_args = self._generate_custom_reboots_test_args()
+        if test_args:
+            return test_args
 
-    def test_hard_reboot_dut_ipv4_5g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
+        # Interrupt tests requires using iperf. So do not run interrupt tests
+        # when skip_iperf is True
+        if self.skip_iperf is True:
+            interrupts = [False]
+        else:
+            interrupts = INTERRUPTS
 
-    def test_hard_reboot_dut_ipv6_2g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
+        for (reboot_device, reboot_type, band, ip_version, interrupt,
+             security_mode) in itertools.product(DEVICES, REBOOT_TYPES, BANDS,
+                                                 IP_VERSIONS, interrupts,
+                                                 SECURITY_MODES):
+            settings = {
+                "reboot_device": reboot_device,
+                "reboot_type": reboot_type,
+                "band": band,
+                "security_mode": security_mode,
+                "ipv4": ip_version["ipv4"],
+                "ipv6": ip_version["ipv6"],
+                "interrupt": interrupt,
+            }
+            test_args.append((settings, ))
 
-    def test_hard_reboot_dut_ipv6_5g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
+        return test_args
 
-    def test_hard_reboot_dut_ipv4_ipv6_2g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv4_ipv6_5g(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
-
-# Open Network AP Soft Reboots (requires a DUT and Whirlwind)
-
-    def test_soft_reboot_ap_ipv4_2g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_5g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv6_2g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv6_5g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_ipv6_2g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_ipv6_5g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
-
-# Open Network AP Soft Reboots (requires a DUT and Whirlwind) with traffic
-# interruption.
-
-    def test_soft_reboot_ap_ipv4_2g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_2G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_5g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_5G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv6_2g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_2G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv6_5g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_5G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_ipv6_2g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_ipv6_5g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-# Open Network AP Hard Reboot (requires a DUT, Whirlwind, and PDU)
-
-    def test_hard_reboot_ap_ipv4_2g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_5g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv6_2g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv6_5g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_ipv6_2g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_ipv6_5g(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-        }
-        self.run_reboot_test(settings)
-
-# Open Network AP Hard Reboot (requires a DUT, Whirlwind, and PDU) with traffic
-# interruptions.
-
-    def test_hard_reboot_ap_ipv4_2g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_2G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_5g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_5G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv6_2g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_2G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv6_5g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_5G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_ipv6_2g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_ipv6_5g_interrupt(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-            'interrupt': True
-        }
-        self.run_reboot_test(settings)
-
-# WPA2 DUT Soft Reboots (requires a DUT and Whirlwind)
-
-    def test_soft_reboot_dut_ipv4_2g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_dut_ipv4_5g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_dut_ipv6_2g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_dut_ipv6_5g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_dut_ipv4_ipv6_2g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_dut_ipv4_ipv6_5g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-# WPA2 Network DUT Hard Reboots (requires a DUT, Whirlwind, and PDU)
-
-    def test_hard_reboot_dut_ipv4_2g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv4_5g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv6_2g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv6_5g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv4_ipv6_2g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv4_ipv6_5g_wpa2(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-# WPA2 Network AP Soft Reboots (requires a DUT and Whirlwind)
-
-    def test_soft_reboot_ap_ipv4_2g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_5g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv6_2g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv6_5g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_ipv6_2g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_ipv6_5g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-# WPA2 Network AP Hard Reboot (requires a DUT, Whirlwind, and PDU)
-
-    def test_hard_reboot_ap_ipv4_2g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_5g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv6_2g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv6_5g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_ipv6_2g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_ipv6_5g_wpa2(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA2_STRING
-        }
-        self.run_reboot_test(settings)
-
-# WPA3 DUT Soft Reboots (requires a DUT and Whirlwind)
-
-    def test_soft_reboot_dut_ipv4_2g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_dut_ipv4_5g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_dut_ipv6_2g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_dut_ipv6_5g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_dut_ipv4_ipv6_2g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_dut_ipv4_ipv6_5g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-# WPA3 Network DUT Hard Reboots (requires a DUT, Whirlwind, and PDU)
-
-    def test_hard_reboot_dut_ipv4_2g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv4_5g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv6_2g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv6_5g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv4_ipv6_2g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_dut_ipv4_ipv6_5g_wpa3(self):
-        settings = {
-            'reboot_device': DUT,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-# WPA3 Network AP Soft Reboots (requires a DUT and Whirlwind)
-
-    def test_soft_reboot_ap_ipv4_2g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_5g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv6_2g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv6_5g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_ipv6_2g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_soft_reboot_ap_ipv4_ipv6_5g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': SOFT,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-
-# WPA3 Network AP Hard Reboot (requires a DUT, Whirlwind, and PDU)
-
-    def test_hard_reboot_ap_ipv4_2g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_5g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv6_2g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv6_5g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_ipv6_2g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_2G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_hard_reboot_ap_ipv4_ipv6_5g_wpa3(self):
-        settings = {
-            'reboot_device': AP,
-            'reboot_type': HARD,
-            'ipv4': True,
-            'ipv6': True,
-            'band': BAND_5G,
-            'security_mode': hostapd_constants.WPA3_STRING
-        }
-        self.run_reboot_test(settings)
-
-    def test_custom_reboots(self):
+    def _generate_custom_reboots_test_args(self):
         """Used to create custom reboot tests from ACTS config. Can be
         individual tests or permutation sets (i.e. setting "all" for a
         test param will run a test with every permutation).
@@ -1578,13 +678,11 @@
             security_modes: optional, string or list - "open", "wep", "wpa",
                 "wpa2", "wpa/wpa2", "wpa3", "wpa2/wpa3"
             iterations: int - number of iterations for each test
-            test_name: string (optional, one will be generated)
 
         Example:
         "wlan_reboot_test_params": {
             "test_custom_reboots": [
                 {
-                    "test_name": "test_custom_soft_reboot_dut_2g_dual_ip_open",
                     "reboot_device": "dut",
                     "reboot_type": "soft",
                     "band": "2g",
@@ -1599,7 +697,6 @@
                     "iterations": 10
                 },
                 {
-                    "test_name": "test_custom_hard_reboot_dut_open_and_wpa3",
                     "reboot_device": "dut",
                     "reboot_type": "hard",
                     "band": "5g",
@@ -1618,10 +715,11 @@
         The third example runs two tests, both hard reboots of the DUT with 5g
         and ipv4 only, one with open security and one with WPA3.
         """
-        asserts.skip_if(
-            'test_custom_reboots' not in self.wlan_reboot_test_params,
-            'No custom reboots provided in ACTS config.')
-        test_list = []
+        if 'test_custom_reboots' not in self.wlan_reboot_test_params:
+            self.log.info('No custom reboots provided in ACTS config.')
+            return []
+
+        test_args = []
         for test in self.wlan_reboot_test_params['test_custom_reboots']:
             # Ensure required params are present
             try:
@@ -1639,6 +737,12 @@
             interrupt = str(test.get('interrupt', False)).lower()
             iterations = test.get('iterations', 1)
 
+            if interrupt == "true" and self.skip_iperf:
+                raise AttributeError(
+                    "Interrupt can't be set to True when iperf is disabled. "
+                    "Update 'skip_iperf' to 'false' in ACTS config and run again"
+                )
+
             # Validate parameters and convert to lists (for permutations)
             try:
                 reboot_devices = CUSTOM_TEST_REBOOT_DEVICES[reboot_device]
@@ -1648,59 +752,36 @@
                 interrupts = CUSTOM_TEST_INTERRUPTS[interrupt]
                 if isinstance(security_modes, str):
                     security_modes = [security_modes]
-
             except KeyError as err:
                 raise AttributeError(
                     'Invalid custom test parameter provided. Err: %s' % err)
 
-            # Generate base test name if one is not present
-            if 'test_name' in test:
-                test_name = test['test_name']
-            else:
-                test_name = create_custom_test_name(reboot_device, reboot_type,
-                                                    band, ip_version,
-                                                    interrupt, iterations)
+            for (reboot_device, reboot_type, band, ip_version, interrupt,
+                 security_mode) in itertools.product(reboot_devices,
+                                                     reboot_types, bands,
+                                                     ip_versions, interrupts,
+                                                     security_modes):
+                settings = {
+                    "reboot_device": reboot_device,
+                    "reboot_type": reboot_type,
+                    "band": band,
+                    "security_mode": security_mode,
+                    "ipv4": ip_version[IPV4],
+                    "ipv6": ip_version[IPV6],
+                    "interrupt": interrupt,
+                    "iterations": iterations,
+                }
 
-            test_settings = {
-                'test_name': test_name,
-                'reboot_devices': reboot_devices,
-                'reboot_types': reboot_types,
-                'bands': bands,
-                'ip_versions': ip_versions,
-                'security_modes': security_modes,
-                'interrupts': interrupts,
-                'iterations': iterations
-            }
-            test_list.append(test_settings)
+                test_args.append((settings, ))
+        return test_args
 
-        # Run all permutations of the test settings
-        self.run_generated_testcases(self.run_custom_test_permutations,
-                                     test_list,
-                                     name_func=get_test_name)
-
-    def run_custom_test_permutations(self, settings):
-        """Runs a custom reboot subtest for each permutation of the provided
-        test parameters."""
-        test_list = []
-        for combination in itertools.product(
-                settings['reboot_devices'],
-                settings['reboot_types'],
-                settings['bands'],
-                settings['ip_versions'],
-                settings['security_modes'],
-                settings['interrupts'],
-        ):
-            test_settings = {
-                'reboot_device': combination[0],
-                'reboot_type': combination[1],
-                'band': combination[2],
-                'ipv4': combination[3][IPV4],
-                'ipv6': combination[3][IPV6],
-                'security_mode': combination[4],
-                'interrupt': combination[5],
-                'iterations': settings['iterations']
-            }
-            test_list.append(test_settings)
-        self.run_generated_testcases(self.run_reboot_test,
-                                     test_list,
-                                     name_func=create_custom_subtest_name)
+    def _read_wlan_reboot_test_params(self):
+        self.wlan_reboot_test_params = self.user_params.get(
+            'wlan_reboot_test_params', {})
+        self.skip_iperf = self.wlan_reboot_test_params.get('skip_iperf', False)
+        # Times (in seconds) to wait for DUT network connection and assigning an
+        # ip address to the wlan interface.
+        self.dut_network_connection_timeout = self.wlan_reboot_test_params.get(
+            'dut_network_connection_timeout', DUT_NETWORK_CONNECTION_TIMEOUT)
+        self.dut_ip_address_timeout = self.wlan_reboot_test_params.get(
+            'dut_ip_address_timeout', IP_ADDRESS_TIMEOUT)
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/WlanScanTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/WlanScanTest.py
index 6133f0b..4185e9c 100644
--- a/acts_tests/tests/google/fuchsia/wlan/functional/WlanScanTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/WlanScanTest.py
@@ -21,20 +21,15 @@
 
 from datetime import datetime
 
-import pprint
-import time
-
-import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
-
 from acts import signals
 from acts.controllers.ap_lib import hostapd_ap_preset
 from acts.controllers.ap_lib import hostapd_bss_settings
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib import hostapd_security
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 
 
-class WlanScanTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanScanTest(WifiBaseTest):
     """WLAN scan test class.
 
     Test Bed Requirement:
@@ -42,6 +37,7 @@
     * Several Wi-Fi networks visible to the device, including an open Wi-Fi
       network or a onHub/GoogleWifi
     """
+
     def setup_class(self):
         super().setup_class()
 
@@ -146,7 +142,7 @@
 
     def teardown_test(self):
         for fd in self.fuchsia_devices:
-            fd.wlan_lib.wlanDisconnect()
+            fd.sl4f.wlan_lib.wlanDisconnect()
 
     def teardown_class(self):
         if self.start_access_point:
@@ -195,8 +191,8 @@
         if 'password' in wlan_network_params:
             target_pwd = wlan_network_params['password']
 
-        bss_scan_response = fd.wlan_lib.wlanScanForBSSInfo().get('result')
-        connection_response = fd.wlan_lib.wlanConnectToNetwork(
+        bss_scan_response = fd.sl4f.wlan_lib.wlanScanForBSSInfo().get('result')
+        connection_response = fd.sl4f.wlan_lib.wlanConnectToNetwork(
             target_ssid,
             bss_scan_response[target_ssid][0],
             target_pwd=target_pwd)
@@ -210,7 +206,7 @@
         """
         start_time = datetime.now()
 
-        scan_response = fd.wlan_lib.wlanStartScan()
+        scan_response = fd.sl4f.wlan_lib.wlanStartScan()
 
         # first check if we received an error
         if scan_response.get("error") is None:
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/WlanTargetSecurityTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/WlanTargetSecurityTest.py
index 2debf65..802fd19 100644
--- a/acts_tests/tests/google/fuchsia/wlan/functional/WlanTargetSecurityTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/WlanTargetSecurityTest.py
@@ -19,19 +19,20 @@
 from acts.controllers.access_point import setup_ap
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib.hostapd_security import Security
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 
 
 # TODO(fxb/68956): Add security protocol check to mixed mode tests when info is
 # available.
-class WlanTargetSecurityTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanTargetSecurityTest(WifiBaseTest):
     """Tests Fuchsia's target security concept and security upgrading
 
     Testbed Requirements:
     * One Fuchsia device
     * One Whirlwind Access Point
     """
+
     def setup_class(self):
         if 'dut' in self.user_params and self.user_params[
                 'dut'] != 'fuchsia_devices':
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/WlanWirelessNetworkManagementTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/WlanWirelessNetworkManagementTest.py
new file mode 100644
index 0000000..5ee9d03
--- /dev/null
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/WlanWirelessNetworkManagementTest.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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 datetime import datetime, timedelta, timezone
+from typing import FrozenSet
+
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
+from acts import asserts
+from acts import signals
+from acts import utils
+from acts.controllers.access_point import setup_ap
+from acts.controllers.ap_lib import hostapd_constants
+from acts.controllers.ap_lib.radio_measurement import BssidInformation, BssidInformationCapabilities, NeighborReportElement, PhyType
+from acts.controllers.ap_lib.wireless_network_management import BssTransitionManagementRequest
+
+
+# TODO(fxbug.dev/103440) WNM support should be visible/controllable in ACTS.
+# When ACTS can see WNM features that are enabled (through ACTS config) or
+# ACTS can enable WNM features (through new APIs), additional tests should be
+# added to this suite that check that features function properly when the DUT is
+# configured to support those features.
+class WlanWirelessNetworkManagementTest(WifiBaseTest):
+    """Tests Fuchsia's Wireless Network Management (AKA 802.11v) support.
+
+    Testbed Requirements:
+    * One Fuchsia device
+    * One Whirlwind access point
+
+    Existing Fuchsia drivers do not yet support WNM features out-of-the-box, so these
+    tests check that WNM features are not enabled.
+    """
+
+    def setup_class(self):
+        if 'dut' in self.user_params and self.user_params[
+                'dut'] != 'fuchsia_devices':
+            raise AttributeError(
+                'WlanWirelessNetworkManagementTest is only relevant for Fuchsia devices.'
+            )
+
+        self.dut = create_wlan_device(self.fuchsia_devices[0])
+        if self.dut.device.association_mechanism != 'policy':
+            raise AttributeError('Must use WLAN policy layer to test WNM.')
+        self.access_point = self.access_points[0]
+
+    def teardown_class(self):
+        self.dut.disconnect()
+        self.access_point.stop_all_aps()
+
+    def teardown_test(self):
+        self.dut.disconnect()
+        self.download_ap_logs()
+        self.access_point.stop_all_aps()
+
+    def on_fail(self, test_name: str, begin_time: str):
+        super().on_fail(test_name, begin_time)
+        self.access_point.stop_all_aps()
+
+    def on_exception(self, test_name: str, begin_time: str):
+        super().on_exception(test_name, begin_time)
+        self.dut.disconnect()
+        self.access_point.stop_all_aps()
+
+    def setup_ap(
+        self,
+        ssid: str,
+        channel: int = hostapd_constants.AP_DEFAULT_CHANNEL_2G,
+        wnm_features: FrozenSet[hostapd_constants.WnmFeature] = frozenset()):
+        """Sets up an AP using the provided parameters.
+
+        Args:
+            ssid: SSID for the AP.
+            channel: which channel number to set the AP to (default is
+                AP_DEFAULT_CHANNEL_2G).
+            wnm_features: Wireless Network Management features to enable
+                (default is no WNM features).
+        """
+        setup_ap(access_point=self.access_point,
+                 profile_name='whirlwind',
+                 channel=channel,
+                 ssid=ssid,
+                 security=None,
+                 wnm_features=wnm_features)
+
+    def _get_client_mac(self) -> str:
+        """Get the MAC address of the DUT client interface.
+
+        Returns:
+            str, MAC address of the DUT client interface.
+        Raises:
+            ValueError if there is no DUT client interface.
+            ConnectionError if the DUT interface query fails.
+        """
+        wlan_ifaces = self.dut.device.sl4f.wlan_lib.wlanGetIfaceIdList()
+        if wlan_ifaces.get('error'):
+            raise ConnectionError('Failed to get wlan interface IDs: %s' %
+                                  wlan_ifaces['error'])
+
+        for wlan_iface in wlan_ifaces['result']:
+            iface_info = self.dut.device.sl4f.wlan_lib.wlanQueryInterface(
+                wlan_iface)
+            if iface_info.get('error'):
+                raise ConnectionError('Failed to query wlan iface: %s' %
+                                      iface_info['error'])
+
+            if iface_info['result']['role'] == 'Client':
+                return utils.mac_address_list_to_str(
+                    iface_info['result']['sta_addr'])
+        raise ValueError(
+            'Failed to get client interface mac address. No client interface found.'
+        )
+
+    def test_bss_transition_ap_supported_dut_unsupported(self):
+        ssid = utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G)
+        wnm_features = frozenset(
+            [hostapd_constants.WnmFeature.BSS_TRANSITION_MANAGEMENT])
+        self.setup_ap(ssid, wnm_features=wnm_features)
+        asserts.assert_true(self.dut.associate(ssid), 'Failed to associate.')
+        asserts.assert_true(self.dut.is_connected(), 'Failed to connect.')
+        client_mac = self._get_client_mac()
+
+        ext_capabilities = self.access_point.get_sta_extended_capabilities(
+            self.access_point.wlan_2g, client_mac)
+        asserts.assert_false(
+            ext_capabilities.bss_transition,
+            'DUT is incorrectly advertising BSS Transition Management support')
+
+    def test_wnm_sleep_mode_ap_supported_dut_unsupported(self):
+        ssid = utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G)
+        wnm_features = frozenset([hostapd_constants.WnmFeature.WNM_SLEEP_MODE])
+        self.setup_ap(ssid, wnm_features=wnm_features)
+        asserts.assert_true(self.dut.associate(ssid), 'Failed to associate.')
+        asserts.assert_true(self.dut.is_connected(), 'Failed to connect.')
+        client_mac = self._get_client_mac()
+
+        ext_capabilities = self.access_point.get_sta_extended_capabilities(
+            self.access_point.wlan_2g, client_mac)
+        asserts.assert_false(
+            ext_capabilities.wnm_sleep_mode,
+            'DUT is incorrectly advertising WNM Sleep Mode support')
+
+    def test_btm_req_ignored_dut_unsupported(self):
+        ssid = utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G)
+        wnm_features = frozenset(
+            [hostapd_constants.WnmFeature.BSS_TRANSITION_MANAGEMENT])
+        # Setup 2.4 GHz AP.
+        self.setup_ap(ssid,
+                      channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
+                      wnm_features=wnm_features)
+
+        asserts.assert_true(self.dut.associate(ssid), 'Failed to associate.')
+        # Verify that DUT is actually associated (as seen from AP).
+        client_mac = self._get_client_mac()
+        asserts.assert_true(
+            client_mac
+            in self.access_point.get_stas(self.access_point.wlan_2g),
+            'Client MAC not included in list of associated STAs on the 2.4GHz band'
+        )
+
+        # Setup 5 GHz AP with same SSID.
+        self.setup_ap(ssid,
+                      channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
+                      wnm_features=wnm_features)
+
+        # Construct a BTM request.
+        dest_bssid = self.access_point.get_bssid_from_ssid(
+            ssid, self.access_point.wlan_5g)
+        dest_bssid_info = BssidInformation(
+            security=True, capabilities=BssidInformationCapabilities())
+        neighbor_5g_ap = NeighborReportElement(
+            dest_bssid,
+            dest_bssid_info,
+            operating_class=126,
+            channel_number=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
+            phy_type=PhyType.VHT)
+        btm_req = BssTransitionManagementRequest(
+            disassociation_imminent=True, candidate_list=[neighbor_5g_ap])
+
+        # Send BTM request from 2.4 GHz AP to DUT
+        self.access_point.send_bss_transition_management_req(
+            self.access_point.wlan_2g, client_mac, btm_req)
+
+        # Check that DUT has not reassociated.
+        REASSOC_DEADLINE = datetime.now(timezone.utc) + timedelta(seconds=2)
+        while datetime.now(timezone.utc) < REASSOC_DEADLINE:
+            # Fail if DUT has reassociated to 5 GHz AP (as seen from AP).
+            if client_mac in self.access_point.get_stas(
+                    self.access_point.wlan_5g):
+                raise signals.TestFailure(
+                    'DUT unexpectedly roamed to target BSS after BTM request')
+            else:
+                time.sleep(0.25)
+
+        # DUT should have stayed associated to original AP.
+        asserts.assert_true(
+            client_mac
+            in self.access_point.get_stas(self.access_point.wlan_2g),
+            'DUT lost association on the 2.4GHz band after BTM request')
diff --git a/acts_tests/tests/google/fuchsia/wlan/misc/WlanInterfaceTest.py b/acts_tests/tests/google/fuchsia/wlan/misc/WlanInterfaceTest.py
index 3e2b707..4ded347 100644
--- a/acts_tests/tests/google/fuchsia/wlan/misc/WlanInterfaceTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/misc/WlanInterfaceTest.py
@@ -16,11 +16,12 @@
 
 from acts import signals
 
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 
 
-class WlanInterfaceTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanInterfaceTest(WifiBaseTest):
+
     def setup_class(self):
         super().setup_class()
         dut = self.user_params.get('dut', None)
diff --git a/acts_tests/tests/google/fuchsia/wlan/misc/WlanMiscScenarioTest.py b/acts_tests/tests/google/fuchsia/wlan/misc/WlanMiscScenarioTest.py
index 950015d..c6c9a33 100644
--- a/acts_tests/tests/google/fuchsia/wlan/misc/WlanMiscScenarioTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/misc/WlanMiscScenarioTest.py
@@ -20,11 +20,11 @@
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib.hostapd_utils import generate_random_password
 from acts.controllers.ap_lib.hostapd_security import Security
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 
 
-class WlanMiscScenarioTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanMiscScenarioTest(WifiBaseTest):
     """Random scenario tests, usually to reproduce certain bugs, that do not
     fit into a specific test category, but should still be run in CI to catch
     regressions.
diff --git a/acts_tests/tests/google/fuchsia/wlan/performance/ChannelSweepTest.py b/acts_tests/tests/google/fuchsia/wlan/performance/ChannelSweepTest.py
index 8ea7891..c12a3f5 100644
--- a/acts_tests/tests/google/fuchsia/wlan/performance/ChannelSweepTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/performance/ChannelSweepTest.py
@@ -14,13 +14,11 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import json
 import os
 import time
 
 from statistics import pstdev
 
-from bokeh.models import FixedTicker
 from bokeh.plotting import ColumnDataSource
 from bokeh.plotting import figure
 from bokeh.plotting import output_file
@@ -34,9 +32,8 @@
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib.hostapd_security import Security
 from acts.controllers.iperf_server import IPerfResult
-from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
 
 N_CAPABILITIES_DEFAULT = [
     hostapd_constants.N_CAPABILITY_LDPC, hostapd_constants.N_CAPABILITY_SGI20,
@@ -76,7 +73,7 @@
     return settings.get('test_name')
 
 
-class ChannelSweepTest(AbstractDeviceWlanDeviceBaseTest):
+class ChannelSweepTest(WifiBaseTest):
     """Tests channel performance and regulatory compliance..
 
     Testbed Requirement:
@@ -85,8 +82,9 @@
     * One Linux Machine used as IPerfServer if running performance tests
     Note: Performance tests should be done in isolated testbed.
     """
+
     def __init__(self, controllers):
-        WifiBaseTest.__init__(self, controllers)
+        super().__init__(controllers)
         if 'channel_sweep_test_params' in self.user_params:
             self.time_to_wait_for_ip_addr = self.user_params[
                 'channel_sweep_test_params'].get(
@@ -186,7 +184,7 @@
             country_code: string, the 2 character country code to set
         """
         self.log.info('Setting DUT country code to %s' % country_code)
-        country_code_response = self.dut.device.regulatory_region_lib.setRegion(
+        country_code_response = self.dut.device.sl4f.regulatory_region_lib.setRegion(
             country_code)
         if country_code_response.get('error'):
             raise EnvironmentError(
@@ -195,7 +193,7 @@
 
         self.log.info('Verifying DUT country code was correctly set to %s.' %
                       country_code)
-        phy_ids_response = self.dut.device.wlan_lib.wlanPhyIdList()
+        phy_ids_response = self.dut.device.sl4f.wlan_lib.wlanPhyIdList()
         if phy_ids_response.get('error'):
             raise ConnectionError('Failed to get phy ids from DUT. Error: %s' %
                                   (country_code, phy_ids_response['error']))
@@ -203,7 +201,7 @@
         end_time = time.time() + TIME_TO_WAIT_FOR_COUNTRY_CODE
         while time.time() < end_time:
             for id in phy_ids_response['result']:
-                get_country_response = self.dut.device.wlan_lib.wlanGetCountry(
+                get_country_response = self.dut.device.sl4f.wlan_lib.wlanGetCountry(
                     id)
                 if get_country_response.get('error'):
                     raise ConnectionError(
diff --git a/acts_tests/tests/google/fuchsia/wlan/performance/WlanRvrTest.py b/acts_tests/tests/google/fuchsia/wlan/performance/WlanRvrTest.py
index 545bbc5..65c0360 100644
--- a/acts_tests/tests/google/fuchsia/wlan/performance/WlanRvrTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/performance/WlanRvrTest.py
@@ -26,9 +26,8 @@
 from acts.controllers.ap_lib.hostapd_security import Security
 from acts.controllers.attenuator import get_attenuators_for_device
 from acts.controllers.iperf_server import IPerfResult
-from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
 from acts.utils import rand_ascii_str
 
 from bokeh.plotting import ColumnDataSource
@@ -37,7 +36,6 @@
 from bokeh.plotting import save
 
 AP_11ABG_PROFILE_NAME = 'whirlwind_11ag_legacy'
-RADVD_PREFIX = 'fd00::/64'
 REPORTING_SPEED_UNITS = 'Mbps'
 
 RVR_GRAPH_SUMMARY_FILE = 'rvr_summary.html'
@@ -101,7 +99,7 @@
                                           throughput[csv_loop_counter]))
 
 
-class WlanRvrTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanRvrTest(WifiBaseTest):
     """Tests running WLAN RvR.
 
     Test Bed Requirement:
@@ -112,11 +110,11 @@
     """
 
     def __init__(self, controllers):
-        WifiBaseTest.__init__(self, controllers)
+        super().__init__(controllers)
         self.rvr_graph_summary = []
 
     def setup_class(self):
-        super(WlanRvrTest, self).setup_class()
+        super().setup_class()
         if 'dut' in self.user_params:
             if self.user_params['dut'] == 'fuchsia_devices':
                 self.dut = create_wlan_device(self.fuchsia_devices[0])
@@ -210,9 +208,7 @@
                         title='RvR Sumamry')
             save(list(self.rvr_graph_summary))
         except Exception as e:
-            self.log.info('Unable to generate RvR summary file due '
-                          'to Exception')
-            self.log.info(e)
+            self.log.error(f'Unable to generate RvR summary file: {e}')
 
         super().teardown_class()
 
@@ -262,7 +258,7 @@
             iperf_server_ip_addresses = (
                 self.iperf_server.get_interface_ip_addresses(
                     self.iperf_server.test_interface))
-            dut_ip_addresses = self.dut.get_interface_ip_addresses(
+            dut_ip_addresses = self.dut.device.get_interface_ip_addresses(
                 self.dut_iperf_client.test_interface)
 
             self.log.info(
@@ -289,6 +285,7 @@
             'IPv4 addresses are not available on both the DUT and iperf server.'
         )
 
+    # TODO (b/258264565): Merge with fuchsia_device wait_for_ipv6_addr.
     def _wait_for_dad(self, device, test_interface):
         """Wait for Duplicate Address Detection to resolve so that an
         private-local IPv6 address is available for test.
@@ -351,17 +348,13 @@
         else:
             raise ValueError('Invalid WLAN band specified: %s' % band)
         if ip_version == 6:
-            radvd_config = RadvdConfig(
-                prefix=RADVD_PREFIX,
-                adv_send_advert=radvd_constants.ADV_SEND_ADVERT_ON,
-                adv_on_link=radvd_constants.ADV_ON_LINK_ON,
-                adv_autonomous=radvd_constants.ADV_AUTONOMOUS_ON)
             self.router_adv_daemon = Radvd(
                 self.access_point.ssh,
                 self.access_point.interfaces.get_bridge_interface()[0])
+            radvd_config = RadvdConfig()
             self.router_adv_daemon.start(radvd_config)
 
-        for rvr_loop_counter in range(0, self.debug_loop_count):
+        for _ in range(0, self.debug_loop_count):
             for rvr_attenuator in rvr_attenuators:
                 rvr_attenuator.set_atten(self.starting_attn)
 
@@ -394,7 +387,7 @@
                 self.log.info('Waiting for DUT to complete Duplicate Address '
                               'Detection for "{}"...'.format(
                                   self.dut_iperf_client.test_interface))
-                _ = self._wait_for_dad(self.dut,
+                _ = self._wait_for_dad(self.dut.device,
                                        self.dut_iperf_client.test_interface)
             else:
                 raise ValueError('Invalid IP version: {}'.format(ip_version))
@@ -474,9 +467,10 @@
             try:
                 for attenuator in rvr_attenuators:
                     attenuator.set_atten(step)
-            except ValueError:
-                self.log.info('%s is beyond the max or min of the testbed '
-                              'attenuator\'s capability. Stopping.')
+            except ValueError as e:
+                self.log.error(
+                    f'{step} is beyond the max or min of the testbed '
+                    f'attenuator\'s capability. Stopping. {e}')
                 break
             self.log.info('Set relative attenuation to %s db' % step)
 
@@ -514,7 +508,7 @@
                     relative_attn.append(value_to_insert)
                     attn_value_inserted = True
 
-            dut_ip_addresses = self.dut.get_interface_ip_addresses(
+            dut_ip_addresses = self.dut.device.get_interface_ip_addresses(
                 self.dut_iperf_client.test_interface)
             if ip_version == 4:
                 if not dut_ip_addresses['ipv4_private']:
@@ -567,10 +561,11 @@
                         '%s_%s_%s' %
                         (iperf_tag, traffic_dir, self.starting_attn),
                         timeout=(self.dwell_time_in_secs * 2))
-                except TimeoutError:
+                except TimeoutError as e:
                     iperf_results_file = None
-                    self.log.info('Iperf traffic timed out. Marking 0 %s for '
-                                  'throughput.' % REPORTING_SPEED_UNITS)
+                    self.log.error(
+                        f'Iperf traffic timed out. Marking 0 {REPORTING_SPEED_UNITS} for '
+                        f'throughput. {e}')
 
                 if not iperf_results_file:
                     throughput.append(0)
@@ -582,24 +577,26 @@
                         if iperf_results.error:
                             self.iperf_server.stop()
                             self.iperf_server.start()
-                            self.log.info('\nErrors in iperf logs:\n%s' %
-                                          iperf_results.error)
+                            self.log.error(
+                                f'Errors in iperf logs:\n{iperf_results.error}'
+                            )
                         if not iperf_results.avg_send_rate:
                             throughput.append(0)
                         else:
                             throughput.append(iperf_results.avg_send_rate)
-                    except ValueError:
+                    except ValueError as e:
                         self.iperf_server.stop()
                         self.iperf_server.start()
-                        self.log.info(
-                            'No data in Iperf file. Marking 0 %s for '
-                            'throughput.' % REPORTING_SPEED_UNITS)
+                        self.log.error(
+                            f'No data in iPerf3 file. Marking 0 {REPORTING_SPEED_UNITS} '
+                            f'for throughput: {e}')
                         throughput.append(0)
                     except Exception as e:
                         self.iperf_server.stop()
                         self.iperf_server.start()
-                        self.log.info('Unknown exception. Marking 0 %s for '
-                                      'throughput.' % REPORTING_SPEED_UNITS)
+                        self.log.error(
+                            f'Unknown exception. Marking 0 {REPORTING_SPEED_UNITS} for '
+                            f'throughput: {e}')
                         self.log.error(e)
                         throughput.append(0)
 
diff --git a/acts_tests/tests/google/fuchsia/wlan/performance/WlanWmmTest.py b/acts_tests/tests/google/fuchsia/wlan/performance/WlanWmmTest.py
index adbb5f6..b57d555 100644
--- a/acts_tests/tests/google/fuchsia/wlan/performance/WlanWmmTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/performance/WlanWmmTest.py
@@ -29,8 +29,8 @@
 from acts.controllers.ap_lib import hostapd_security
 from acts_contrib.test_utils.abstract_devices import wmm_transceiver
 from acts_contrib.test_utils.fuchsia import wmm_test_cases
+from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
-from acts_contrib.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
 
 DEFAULT_N_CAPABILITIES_20_MHZ = [
     hostapd_constants.N_CAPABILITY_LDPC, hostapd_constants.N_CAPABILITY_SGI20,
@@ -102,7 +102,7 @@
     return error <= accepted_error
 
 
-class WlanWmmTest(AbstractDeviceWlanDeviceBaseTest):
+class WlanWmmTest(WifiBaseTest):
     """Tests WMM QoS Functionality (Station only)
 
     Testbed Requirements:
@@ -112,6 +112,7 @@
 
     For accurate results, must be performed in an RF isolated environment.
     """
+
     def setup_class(self):
         super().setup_class()
 
@@ -659,6 +660,7 @@
 # External Traffic Differentiation
 
     """Single station, STAUT transmits high priority"""
+
     def test_external_traffic_diff_staut_VO_ap_VI(self):
         self.run_wmm_test(
             wmm_test_cases.test_external_traffic_diff_staut_VO_ap_VI)
@@ -770,6 +772,7 @@
 # WFA Test Plan Tests
 
     """Traffic Differentiation in Single BSS (Single Station)"""
+
     def test_wfa_traffic_diff_single_station_staut_BE_ap_VI_BE(self):
         self.run_wmm_test(
             wmm_test_cases.
diff --git a/acts_tests/tests/google/fuchsia/wlan_policy/HiddenNetworksTest.py b/acts_tests/tests/google/fuchsia/wlan_policy/HiddenNetworksTest.py
index 7d294d2..b5e8d0a 100644
--- a/acts_tests/tests/google/fuchsia/wlan_policy/HiddenNetworksTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan_policy/HiddenNetworksTest.py
@@ -13,13 +13,15 @@
 #   WITHOUT 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.controllers.access_point import setup_ap
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib import hostapd_security
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts.utils import rand_ascii_str
-import time
 
 # These tests should have a longer timeout for connecting than normal connect
 # tests because the device should probabilistically perform active scans for
@@ -96,7 +98,7 @@
 
             while time.time() < start_time + TIME_ATTEMPT_SCANS:
                 num_performed_scans = num_performed_scans + 1
-                scan_result = fd.wlan_policy_lib.wlanScanForNetworks()
+                scan_result = fd.sl4f.wlan_policy_lib.wlanScanForNetworks()
                 if scan_result["error"] != None:
                     self.log.warn("Failed to scan for networks with error %s" %
                                   scan_result["error"])
diff --git a/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py b/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py
index efd8729..99055a7 100644
--- a/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py
@@ -18,7 +18,7 @@
 
 from datetime import datetime
 
-from acts import signals, utils
+from acts import signals
 from acts.controllers.ap_lib import (hostapd_ap_preset, hostapd_bss_settings,
                                      hostapd_constants, hostapd_security)
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
@@ -135,7 +135,7 @@
         """
         start_time = datetime.now()
 
-        scan_response = fd.wlan_policy_lib.wlanScanForNetworks()
+        scan_response = fd.sl4f.wlan_policy_lib.wlanScanForNetworks()
 
         # first check if we received an error
         if scan_response.get("error") is not None:
diff --git a/acts_tests/tests/google/fuchsia/wlan_policy/RegulatoryRecoveryTest.py b/acts_tests/tests/google/fuchsia/wlan_policy/RegulatoryRecoveryTest.py
index 9cfaf84..d07b938 100644
--- a/acts_tests/tests/google/fuchsia/wlan_policy/RegulatoryRecoveryTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan_policy/RegulatoryRecoveryTest.py
@@ -13,9 +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
+
 from acts import signals
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-import time
 
 
 class RegulatoryRecoveryTest(WifiBaseTest):
@@ -68,7 +68,7 @@
             # functionality so that there is no automated WLAN behavior.
             fd.wlan_controller.set_country_code("WW")
             fd.wlan_policy_controller.stop_client_connections()
-            fd.wlan_ap_policy_lib.wlanStopAllAccessPoint()
+            fd.sl4f.wlan_ap_policy_lib.wlanStopAllAccessPoint()
 
     def set_country_code(self, fd):
         try:
@@ -101,11 +101,11 @@
             self.set_country_code(fd)
 
             # Reset the listeners and verify the current state.
-            fd.wlan_policy_lib.wlanSetNewListener()
-            fd.wlan_ap_policy_lib.wlanSetNewListener()
+            fd.sl4f.wlan_policy_lib.wlanSetNewListener()
+            fd.sl4f.wlan_ap_policy_lib.wlanSetNewListener()
 
             # Verify that the client and AP are still stopped.
-            client_state = fd.wlan_policy_lib.wlanGetUpdate()
+            client_state = fd.sl4f.wlan_policy_lib.wlanGetUpdate()
             if client_state["error"]:
                 raise signals.TestFailure(
                     "error querying client state: {}".format(
@@ -115,7 +115,7 @@
                     "client connections in unexpected state: {}".format(
                         client_state["result"]["state"]))
 
-            ap_state = fd.wlan_ap_policy_lib.wlanGetUpdate()
+            ap_state = fd.sl4f.wlan_ap_policy_lib.wlanGetUpdate()
             if ap_state["error"]:
                 raise signals.TestFailure("error querying AP state: {}".format(
                     ap_state["error"]))
@@ -136,20 +136,19 @@
             # Start client connections and start an AP before setting the
             # country code.
             fd.wlan_policy_controller.start_client_connections()
-            fd.wlan_ap_policy_lib.wlanStartAccessPoint(test_ssid,
-                                                       test_security_type, "",
-                                                       "local_only", "any")
+            fd.sl4f.wlan_ap_policy_lib.wlanStartAccessPoint(
+                test_ssid, test_security_type, "", "local_only", "any")
 
             # Set the country code.
             self.set_country_code(fd)
 
             # Reset the listeners and verify the current state.
-            fd.wlan_policy_lib.wlanSetNewListener()
-            fd.wlan_ap_policy_lib.wlanSetNewListener()
+            fd.sl4f.wlan_policy_lib.wlanSetNewListener()
+            fd.sl4f.wlan_ap_policy_lib.wlanSetNewListener()
 
             # Verify that client connections are enabled and the AP is brought
             # up again.
-            client_state = fd.wlan_policy_lib.wlanGetUpdate()
+            client_state = fd.sl4f.wlan_policy_lib.wlanGetUpdate()
             if client_state["error"]:
                 raise signals.TestFailure(
                     "error querying client state: {}".format(
@@ -159,7 +158,7 @@
                     "client connections in unexpected state: {}".format(
                         client_state["result"]["state"]))
 
-            ap_state = fd.wlan_ap_policy_lib.wlanGetUpdate()
+            ap_state = fd.sl4f.wlan_ap_policy_lib.wlanGetUpdate()
             if ap_state["error"]:
                 raise signals.TestFailure("error querying AP state: {}".format(
                     ap_state["error"]))
diff --git a/acts_tests/tests/google/fuchsia/wlan_policy/SavedNetworksTest.py b/acts_tests/tests/google/fuchsia/wlan_policy/SavedNetworksTest.py
index b9d9721..340ca31 100644
--- a/acts_tests/tests/google/fuchsia/wlan_policy/SavedNetworksTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan_policy/SavedNetworksTest.py
@@ -20,14 +20,10 @@
 
 from acts import signals
 from acts.controllers.access_point import setup_ap
-from acts.controllers.ap_lib import hostapd_ap_preset
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib import hostapd_security
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from acts.utils import rand_ascii_str, rand_hex_str, timeout
-import requests
-import time
-import types
+from acts.utils import rand_ascii_str, rand_hex_str
 
 PSK_LEN = 64
 TIME_WAIT_FOR_DISCONNECT = 30
diff --git a/acts_tests/tests/google/fuchsia/wlan_policy/StartStopClientConnectionsTest.py b/acts_tests/tests/google/fuchsia/wlan_policy/StartStopClientConnectionsTest.py
index 7643a05..ab30da5 100644
--- a/acts_tests/tests/google/fuchsia/wlan_policy/StartStopClientConnectionsTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan_policy/StartStopClientConnectionsTest.py
@@ -14,13 +14,14 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import time
+
 from acts import signals
 from acts.controllers.access_point import setup_ap
 from acts.controllers.ap_lib import hostapd_constants
 from acts.controllers.ap_lib import hostapd_security
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts.utils import rand_ascii_str
-import time
 
 DISCONNECTED = "Disconnected"
 CONNECTION_STOPPED = "ConnectionStopped"
@@ -78,7 +79,8 @@
             without error. This does not validate that a connection will be
             attempted. This will fail the test if there is an error sending the
             connect request, or if we don't get the expected connect response."""
-        result_connect = fd.wlan_policy_lib.wlanConnect(ssid, security_type)
+        result_connect = fd.sl4f.wlan_policy_lib.wlanConnect(
+            ssid, security_type)
         if result_connect.get("error") != None:
             self.log.error("Error occurred requesting a connection: %s" %
                            result_connect.get("error"))
@@ -108,8 +110,8 @@
         start_time = time.time()
         curr_state = None
         while time.time() < start_time + timeout:
-            fd.wlan_policy_lib.wlanSetNewListener()
-            curr_state = fd.wlan_policy_lib.wlanGetUpdate()
+            fd.sl4f.wlan_policy_lib.wlanSetNewListener()
+            curr_state = fd.sl4f.wlan_policy_lib.wlanGetUpdate()
             if curr_state.get("error"):
                 self.log.error("Error occurred getting status update: %s" %
                                curr_state.get("error"))
diff --git a/acts_tests/tests/google/gnss/FlpTtffTest.py b/acts_tests/tests/google/gnss/FlpTtffTest.py
index 0a30fe8..2d1f194 100644
--- a/acts_tests/tests/google/gnss/FlpTtffTest.py
+++ b/acts_tests/tests/google/gnss/FlpTtffTest.py
@@ -17,7 +17,6 @@
 from acts import asserts
 from acts import signals
 from acts.base_test import BaseTestClass
-from acts.test_decorators import test_tracker_info
 from acts.utils import get_current_epoch_time
 from acts_contrib.test_utils.wifi.wifi_test_utils import wifi_toggle_state
 from acts_contrib.test_utils.tel.tel_logging_utils import start_qxdm_logger
@@ -40,6 +39,8 @@
 from acts_contrib.test_utils.gnss.gnss_test_utils import connect_to_wifi_network
 from acts_contrib.test_utils.gnss.gnss_test_utils import gnss_tracking_via_gtw_gpstool
 from acts_contrib.test_utils.gnss.gnss_test_utils import parse_gtw_gpstool_log
+from acts_contrib.test_utils.gnss.gnss_test_utils import log_current_epoch_time
+from acts_contrib.test_utils.gnss.testtracker_util import log_testtracker_uuid
 
 
 class FlpTtffTest(BaseTestClass):
@@ -63,6 +64,8 @@
         _init_device(self.ad)
 
     def setup_test(self):
+        log_current_epoch_time(self.ad, "test_start_time")
+        log_testtracker_uuid(self.ad, self.current_test_name)
         get_baseband_and_gms_version(self.ad)
         if self.collect_logs:
             clear_logd_gnss_qxdm_log(self.ad)
@@ -85,6 +88,7 @@
             set_wifi_and_bt_scanning(self.ad, True)
         if self.ad.droid.wifiCheckState():
             wifi_toggle_state(self.ad, False)
+        log_current_epoch_time(self.ad, "test_end_time")
 
     def on_pass(self, test_name, begin_time):
         if self.collect_logs:
@@ -106,11 +110,11 @@
         for mode in ttff.keys():
             begin_time = get_current_epoch_time()
             process_gnss_by_gtw_gpstool(
-                self.ad, self.standalone_cs_criteria, type="flp")
+                self.ad, self.standalone_cs_criteria, api_type="flp")
             start_ttff_by_gtw_gpstool(
                 self.ad, ttff_mode=mode, iteration=self.ttff_test_cycle)
             ttff_data = process_ttff_by_gtw_gpstool(
-                self.ad, begin_time, location, type="flp")
+                self.ad, begin_time, location, api_type="flp")
             result = check_ttff_data(self.ad, ttff_data, ttff[mode], criteria)
             flp_results.append(result)
         asserts.assert_true(
@@ -124,7 +128,6 @@
 
     """ Test Cases """
 
-    @test_tracker_info(uuid="c11ada6a-d7ad-4dc8-9d4a-0ae3cb9dfa8e")
     def test_flp_one_hour_tracking(self):
         """Verify FLP tracking performance of position error.
 
@@ -137,10 +140,9 @@
         """
         self.start_qxdm_and_tcpdump_log()
         gnss_tracking_via_gtw_gpstool(self.ad, self.standalone_cs_criteria,
-                                      type="flp", testtime=60)
-        parse_gtw_gpstool_log(self.ad, self.pixel_lab_location, type="flp")
+                                      api_type="flp", testtime=60)
+        parse_gtw_gpstool_log(self.ad, self.pixel_lab_location, api_type="flp")
 
-    @test_tracker_info(uuid="8bc4e82d-fdce-4ee8-af8c-5e4a925b5360")
     def test_flp_ttff_strong_signal_wifiscan_on_wifi_connect(self):
         """Verify FLP TTFF Hot Start and Cold Start under strong GNSS signals
         with WiFi scanning on and connected.
@@ -163,7 +165,6 @@
         self.flp_ttff_hs_and_cs(self.flp_ttff_max_threshold,
                                 self.pixel_lab_location)
 
-    @test_tracker_info(uuid="adc1a0c7-3635-420d-9481-0f5816c58334")
     def test_flp_ttff_strong_signal_wifiscan_on_wifi_not_connect(self):
         """Verify FLP TTFF Hot Start and Cold Start under strong GNSS signals
         with WiFi scanning on and not connected.
@@ -183,7 +184,6 @@
         self.flp_ttff_hs_and_cs(self.flp_ttff_max_threshold,
                                 self.pixel_lab_location)
 
-    @test_tracker_info(uuid="3ec3cee2-b881-4c61-9df1-b6b81fcd4527")
     def test_flp_ttff_strong_signal_wifiscan_off(self):
         """Verify FLP TTFF Hot Start and Cold Start with WiFi scanning OFF
            under strong GNSS signals.
@@ -202,7 +202,6 @@
         self.flp_ttff_hs_and_cs(self.flp_ttff_max_threshold,
                                 self.pixel_lab_location)
 
-    @test_tracker_info(uuid="03c0d34f-8312-48d5-8753-93b09151233a")
     def test_flp_ttff_weak_signal_wifiscan_on_wifi_connect(self):
         """Verify FLP TTFF Hot Start and Cold Start under Weak GNSS signals
         with WiFi scanning on and connected
@@ -228,7 +227,6 @@
         self.flp_ttff_hs_and_cs(self.flp_ttff_max_threshold,
                                 self.pixel_lab_location)
 
-    @test_tracker_info(uuid="13daf7b3-5ac5-4107-b3dc-a3a8b5589fed")
     def test_flp_ttff_weak_signal_wifiscan_on_wifi_not_connect(self):
         """Verify FLP TTFF Hot Start and Cold Start under Weak GNSS signals
         with WiFi scanning on and not connected.
@@ -251,7 +249,6 @@
         self.flp_ttff_hs_and_cs(self.flp_ttff_max_threshold,
                                 self.pixel_lab_location)
 
-    @test_tracker_info(uuid="1831f80f-099f-46d2-b484-f332046d5a4d")
     def test_flp_ttff_weak_signal_wifiscan_off(self):
         """Verify FLP TTFF Hot Start and Cold Start with WiFi scanning OFF
            under weak GNSS signals.
diff --git a/acts_tests/tests/google/gnss/GnssBlankingThTest.py b/acts_tests/tests/google/gnss/GnssBlankingThTest.py
index e625171..171a453 100644
--- a/acts_tests/tests/google/gnss/GnssBlankingThTest.py
+++ b/acts_tests/tests/google/gnss/GnssBlankingThTest.py
@@ -28,12 +28,14 @@
         first_wait = self.user_params.get('first_wait', 300)
 
         # Start the test item with gnss_init_power_setting.
-        if self.gnss_init_power_setting(first_wait):
-            self.log.info('Successfully set the GNSS power level to %d' %
-                          self.sa_sensitivity)
+        ret, pwr_lvl = self.gnss_init_power_setting(first_wait)
+        if ret:
+            self.log.info(f'Successfully set the GNSS power level to {pwr_lvl}')
             self.log.info('Start searching for cellular power level threshold')
             # After the GNSS power initialization is done, start the cellular power sweep.
             self.result_cell_pwr = self.cell_power_sweep()
+        else:
+            raise AttributeError('Init power sweep is missing')
 
     def test_gnss_gsm850_sweep(self):
         """
diff --git a/acts_tests/tests/google/gnss/GnssUserBuildBroadcomConfigurationTest.py b/acts_tests/tests/google/gnss/GnssBroadcomConfigurationTest.py
similarity index 88%
rename from acts_tests/tests/google/gnss/GnssUserBuildBroadcomConfigurationTest.py
rename to acts_tests/tests/google/gnss/GnssBroadcomConfigurationTest.py
index 9f3ccfd..64fe9c8 100644
--- a/acts_tests/tests/google/gnss/GnssUserBuildBroadcomConfigurationTest.py
+++ b/acts_tests/tests/google/gnss/GnssBroadcomConfigurationTest.py
@@ -8,6 +8,7 @@
 For more details, please refer to : go/p22_user_build_verification
 """
 import os
+import re
 import shutil
 import tempfile
 import time
@@ -15,9 +16,10 @@
 from acts import asserts
 from acts import signals
 from acts.base_test import BaseTestClass
+from acts_contrib.test_utils.gnss.testtracker_util import log_testtracker_uuid
 from acts.controllers.adb_lib.error import AdbCommandError
 from acts.libs.proc.job import TimeoutError
-from acts.test_decorators import test_tracker_info
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
 
 
@@ -147,7 +149,6 @@
         self.lheconsole = "LheConsole"
         self.lheconsole_hub = self.get_lheconsole_value()
         self.esw_crash_dump_pattern = self.get_esw_crash_dump_pattern()
-        self.ad.log.info(f"here is {self.esw_crash_dump_pattern}")
 
     def _adjust_lhe_setting(self, key, enable):
         """Set lhe setting.
@@ -251,14 +252,16 @@
         return False
 
 
-class GnssUserBuildBroadcomConfigurationTest(BaseTestClass):
-    """ GNSS user build configuration Tests on Broadcom device."""
+class GnssBroadcomConfigurationTest(BaseTestClass):
+    """ GNSS configuration Tests on Broadcom device."""
     def setup_class(self):
         super().setup_class()
         self.ad = self.android_devices[0]
+        req_params = ["standalone_cs_criteria"]
+        self.unpack_userparams(req_param_names=req_params)
 
         if not gutils.check_chipset_vendor_by_qualcomm(self.ad):
-            gutils._init_device(self.ad)
+            self.init_device()
             self.gps_config_path = tempfile.mkdtemp()
             self.gps_xml = GpsXml(self.ad)
             self.lhd_conf = LhdConf(self.ad)
@@ -266,19 +269,31 @@
             self.enable_testing_setting()
             self.backup_gps_config()
 
+    def init_device(self):
+        gutils._init_device(self.ad)
+        gutils.enable_supl_mode(self.ad)
+        gutils.enable_vendor_orbit_assistance_data(self.ad)
+        wutils.wifi_toggle_state(self.ad, True)
+        gutils.set_mobile_data(self.ad, state=True)
+
+
     def teardown_class(self):
         if hasattr(self, "gps_config_path") and os.path.isdir(self.gps_config_path):
             shutil.rmtree(self.gps_config_path)
 
     def setup_test(self):
+        gutils.log_current_epoch_time(self.ad, "test_start_time")
+        log_testtracker_uuid(self.ad, self.current_test_name)
         if gutils.check_chipset_vendor_by_qualcomm(self.ad):
             raise signals.TestSkip("Device is Qualcomm, skip the test")
+        gutils.get_baseband_and_gms_version(self.ad)
         gutils.clear_logd_gnss_qxdm_log(self.ad)
 
     def teardown_test(self):
         if not gutils.check_chipset_vendor_by_qualcomm(self.ad):
             self.revert_gps_config()
             self.ad.reboot()
+        gutils.log_current_epoch_time(self.ad, "test_end_time")
 
     def on_fail(self, test_name, begin_time):
         self.ad.take_bug_report(test_name, begin_time)
@@ -317,10 +332,7 @@
     def run_gps_and_capture_log(self):
         """Enable GPS via gps tool for 15s and capture pixel log"""
         gutils.start_pixel_logger(self.ad)
-        gutils.start_gnss_by_gtw_gpstool(self.ad, state=True)
-        time.sleep(15)
-        gutils.start_gnss_by_gtw_gpstool(self.ad, state=False)
-        gutils.stop_pixel_logger(self.ad)
+        gutils.gnss_tracking_via_gtw_gpstool(self.ad, self.standalone_cs_criteria, testtime=1)
 
     def set_gps_logenabled(self, enable):
         """Set LogEnabled in gps.xml / lhd.conf / scd.conf
@@ -337,44 +349,50 @@
             self.scd_conf.disable_diagnostic_log()
             self.lhd_conf.disable_diagnostic_log()
 
-    @test_tracker_info(uuid="1dd68d9c-38b0-4fbc-8635-1228c72872ff")
     def test_gps_logenabled_setting(self):
         """Verify the LogEnabled setting in gps.xml / scd.conf / lhd.conf
         Steps:
             1. default setting is on in user_debug build
-            2. enable gps for 15s
-            3. assert gps log pattern "slog    :" in pixel logger
+            2. run gps tracking for 1 min
+            3. should find slog in pixel logger log files
             4. disable LogEnabled in all the gps conf
-            5. enable gps for 15s
-            6. assert gps log pattern "slog    :" in pixel logger
+            5. run gps tracking for 1 min
+            6. should not find slog in pixel logger log files
         """
         self.run_gps_and_capture_log()
-        result, _ = gutils.parse_brcm_nmea_log(self.ad, "slog    :", [])
+        pattern = re.compile(f".*slog\s+:.*")
+        result, _ = gutils.parse_brcm_nmea_log(self.ad, pattern, [])
         asserts.assert_true(bool(result), "LogEnabled is set to true, but no gps log was found")
 
         self.set_gps_logenabled(enable=False)
         gutils.clear_logd_gnss_qxdm_log(self.ad)
+        # Removes pixel logger path again in case pixel logger still writes log unexpectedly.
+        gutils.remove_pixel_logger_folder(self.ad)
 
         self.run_gps_and_capture_log()
-        result, _ = gutils.parse_brcm_nmea_log(self.ad, "slog    :", [])
-        asserts.assert_false(bool(result), ("LogEnabled is set to False but still found %d slog",
-                                            len(result)))
+        try:
+            result, _ = gutils.parse_brcm_nmea_log(self.ad, pattern, [])
+            asserts.assert_false(
+                bool(result),
+                ("LogEnabled is set to False but still found %d slog" % len(result)))
+        except FileNotFoundError:
+            self.ad.log.info("Test pass because no BRCM log files/folders was found")
 
-    @test_tracker_info(uuid="152a12e0-7957-47e0-9ea7-14725254fd1d")
     def test_gps_supllogenable_setting(self):
         """Verify SuplLogEnable in gps.xml
         Steps:
             1. default setting is on in user_debug build
             2. remove existing supl log
-            3. enable gps for 15s
+            3. run gps tracking for 1 min
             4. supl log should exist
             5. disable SuplLogEnable in gps.xml
             6. remove existing supl log
-            7. enable gps for 15s
+            7. run gps tracking for 1 min
             8. supl log should not exist
         """
         def is_supl_log_exist_after_supl_request():
             self.gps_xml.remove_supl_logs()
+            self.ad.reboot()
             self.run_gps_and_capture_log()
             return self.gps_xml.is_supl_log_file_exist()
 
@@ -382,12 +400,10 @@
         asserts.assert_true(result, "SuplLogEnable is enable, should find supl log file")
 
         self.gps_xml.disable_supl_log()
-        self.ad.reboot()
 
         result = is_supl_log_exist_after_supl_request()
         asserts.assert_false(result, "SuplLogEnable is disable, should not find supl log file")
 
-    @test_tracker_info(uuid="892d0037-8c0c-45b6-bd0f-9e4073d37232")
     def test_lhe_setting(self):
         """Verify lhefailsafe / lheconsole setting in lhd.conf
         Steps:
diff --git a/acts_tests/tests/google/gnss/GnssConcurrencyTest.py b/acts_tests/tests/google/gnss/GnssConcurrencyTest.py
index 9169f4e..3cbb2a7 100644
--- a/acts_tests/tests/google/gnss/GnssConcurrencyTest.py
+++ b/acts_tests/tests/google/gnss/GnssConcurrencyTest.py
@@ -15,15 +15,17 @@
 #   limitations under the License.
 
 import time
-import datetime
 import re
+import statistics
+from datetime import datetime
 from acts import utils
 from acts import signals
 from acts.base_test import BaseTestClass
-from acts.test_decorators import test_tracker_info
+from acts_contrib.test_utils.gnss.testtracker_util import log_testtracker_uuid
 from acts_contrib.test_utils.tel.tel_logging_utils import start_adb_tcpdump
 from acts_contrib.test_utils.tel.tel_logging_utils import stop_adb_tcpdump
 from acts_contrib.test_utils.tel.tel_logging_utils import get_tcpdump_log
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
 
 CONCURRENCY_TYPE = {
@@ -32,6 +34,24 @@
     "ap_location": "reportLocation"
 }
 
+GPS_XML_CONFIG = {
+    "CS": [
+        '    IgnorePosition=\"true\"\n', '    IgnoreEph=\"true\"\n',
+        '    IgnoreTime=\"true\"\n', '    AsstIgnoreLto=\"true\"\n',
+        '    IgnoreJniTime=\"true\"\n'
+    ],
+    "WS": [
+        '    IgnorePosition=\"true\"\n', '    AsstIgnoreLto=\"true\"\n',
+        '    IgnoreJniTime=\"true\"\n'
+    ],
+    "HS": []
+}
+
+ONCHIP_CONFIG = [
+    '    EnableOnChipStopNotification=\"1\"\n',
+    '    EnableOnChipStopNotification=\"2\"\n'
+]
+
 
 class GnssConcurrencyTest(BaseTestClass):
     """ GNSS Concurrency TTFF Tests. """
@@ -42,15 +62,16 @@
         req_params = [
             "standalone_cs_criteria", "chre_tolerate_rate", "qdsp6m_path",
             "outlier_criteria", "max_outliers", "pixel_lab_location",
-            "max_interval", "onchip_interval"
+            "max_interval", "onchip_interval", "ttff_test_cycle"
         ]
         self.unpack_userparams(req_param_names=req_params)
         gutils._init_device(self.ad)
         self.ad.adb.shell("setprop persist.vendor.radio.adb_log_on 0")
         self.ad.adb.shell("sync")
-        gutils.reboot(self.ad)
 
     def setup_test(self):
+        gutils.log_current_epoch_time(self.ad, "test_start_time")
+        log_testtracker_uuid(self.ad, self.current_test_name)
         gutils.clear_logd_gnss_qxdm_log(self.ad)
         gutils.start_pixel_logger(self.ad)
         start_adb_tcpdump(self.ad)
@@ -62,6 +83,7 @@
     def teardown_test(self):
         gutils.stop_pixel_logger(self.ad)
         stop_adb_tcpdump(self.ad)
+        gutils.log_current_epoch_time(self.ad, "test_end_time")
 
     def on_fail(self, test_name, begin_time):
         self.ad.take_bug_report(test_name, begin_time)
@@ -78,8 +100,12 @@
         for _ in range(0, 3):
             try:
                 self.ad.log.info("Start to load the nanoapp")
-                res = self.ad.adb.shell("chre_power_test_client load")
-                if "success: 1" in res:
+                cmd = "chre_power_test_client load"
+                if gutils.is_device_wearable(self.ad):
+                    extra_cmd = "tcm /vendor/etc/chre/power_test_tcm.so"
+                    cmd = " ".join([cmd, extra_cmd])
+                res = self.ad.adb.shell(cmd)
+                if "result 1" in res:
                     self.ad.log.info("Nano app loaded successfully")
                     break
             except Exception as e:
@@ -88,65 +114,79 @@
         else:
             raise signals.TestError("Failed to load CHRE nanoapp")
 
-    def enable_chre(self, freq):
+    def enable_chre(self, interval_sec):
         """ Enable or disable gnss concurrency via nanoapp.
 
         Args:
-            freq: an int for frequency, set 0 as disable.
+            interval_sec: an int for frequency, set 0 as disable.
         """
-        freq = freq * 1000
+        if interval_sec == 0:
+            self.ad.log.info(f"Stop CHRE request")
+        else:
+            self.ad.log.info(
+                f"Initiate CHRE with {interval_sec} seconds interval")
+        interval_msec = interval_sec * 1000
         cmd = "chre_power_test_client"
-        option = "enable %d" % freq if freq != 0 else "disable"
+        option = "enable %d" % interval_msec if interval_msec != 0 else "disable"
 
         for type in CONCURRENCY_TYPE.keys():
             if "ap" not in type:
                 self.ad.adb.shell(" ".join([cmd, type, option]))
 
-    def parse_concurrency_result(self, begin_time, type, criteria):
+    def parse_concurrency_result(self,
+                                 begin_time,
+                                 request_type,
+                                 criteria,
+                                 exam_lower=True):
         """ Parse the test result with given time and criteria.
 
         Args:
             begin_time: test begin time.
-            type: str for location request type.
+            request_type: str for location request type.
             criteria: dictionary for test criteria.
+            exam_lower: a boolean to identify the lower bond or not.
         Return: List for the failure and outlier loops and results.
         """
         results = []
         failures = []
         outliers = []
-        search_results = self.ad.search_logcat(CONCURRENCY_TYPE[type],
+        upper_bound = criteria * (
+            1 + self.chre_tolerate_rate) + self.outlier_criteria
+        lower_bound = criteria * (
+            1 - self.chre_tolerate_rate) - self.outlier_criteria
+        search_results = self.ad.search_logcat(CONCURRENCY_TYPE[request_type],
                                                begin_time)
-        start_time = utils.epoch_to_human_time(begin_time)
-        start_time = datetime.datetime.strptime(start_time,
-                                                "%m-%d-%Y %H:%M:%S ")
         if not search_results:
             raise signals.TestFailure(f"No log entry found for keyword:"
-                                      f"{CONCURRENCY_TYPE[type]}")
-        results.append(
-            (search_results[0]["datetime_obj"] - start_time).total_seconds())
-        samples = len(search_results) - 1
-        for i in range(samples):
+                                      f"{CONCURRENCY_TYPE[request_type]}")
+
+        for i in range(len(search_results) - 1):
             target = search_results[i + 1]
             timedelt = target["datetime_obj"] - search_results[i]["datetime_obj"]
             timedelt_sec = timedelt.total_seconds()
             results.append(timedelt_sec)
-            if timedelt_sec > (criteria *
-                               self.chre_tolerate_rate) + self.outlier_criteria:
-                failures.append(target)
-                self.ad.log.error("[Failure][%s]:%.2f sec" %
-                                  (target["time_stamp"], timedelt_sec))
-            elif timedelt_sec > criteria * self.chre_tolerate_rate:
-                outliers.append(target)
-                self.ad.log.info("[Outlier][%s]:%.2f sec" %
-                                 (target["time_stamp"], timedelt_sec))
+            res_tag = ""
+            if timedelt_sec > upper_bound:
+                failures.append(timedelt_sec)
+                res_tag = "Failure"
+            elif timedelt_sec < lower_bound and exam_lower:
+                failures.append(timedelt_sec)
+                res_tag = "Failure"
+            elif timedelt_sec > criteria * (1 + self.chre_tolerate_rate):
+                outliers.append(timedelt_sec)
+                res_tag = "Outlier"
+            if res_tag:
+                self.ad.log.error(
+                    f"[{res_tag}][{target['time_stamp']}]:{timedelt_sec:.2f} sec"
+                )
 
-        res_summary = " ".join([str(res) for res in results])
-        self.ad.log.info("[%s]Overall Result: %s" % (type, res_summary))
-        self.ad.log.info("TestResult %s_samples %d" % (type, samples))
-        self.ad.log.info("TestResult %s_outliers %d" % (type, len(outliers)))
-        self.ad.log.info("TestResult %s_failures %d" % (type, len(failures)))
-        self.ad.log.info("TestResult %s_max_time %.2f" %
-                         (type, max(results[1:])))
+        res_summary = " ".join([str(res) for res in results[1:]])
+        self.ad.log.info(f"[{request_type}]Overall Result: {res_summary}")
+        log_prefix = f"TestResult {request_type}"
+        self.ad.log.info(f"{log_prefix}_samples {len(search_results)}")
+        self.ad.log.info(f"{log_prefix}_outliers {len(outliers)}")
+        self.ad.log.info(f"{log_prefix}_failures {len(failures)}")
+        self.ad.log.info(f"{log_prefix}_max_time {max(results):.2f}")
 
         return outliers, failures, results
 
@@ -157,12 +197,14 @@
             criteria: int for test criteria.
             test_duration: int for test duration.
         """
-        begin_time = utils.get_current_epoch_time()
-        self.ad.log.info("Tests Start at %s" %
-                         utils.epoch_to_human_time(begin_time))
-        gutils.start_gnss_by_gtw_gpstool(
-            self.ad, True, freq=criteria["ap_location"])
         self.enable_chre(criteria["gnss"])
+        TTFF_criteria = criteria["ap_location"] + self.standalone_cs_criteria
+        gutils.process_gnss_by_gtw_gpstool(
+            self.ad, TTFF_criteria, freq=criteria["ap_location"])
+        self.ad.log.info("Tracking 10 sec to prevent flakiness.")
+        time.sleep(10)
+        begin_time = datetime.now()
+        self.ad.log.info(f"Test Start at {begin_time}")
         time.sleep(test_duration)
         self.enable_chre(0)
         gutils.start_gnss_by_gtw_gpstool(self.ad, False)
@@ -175,9 +217,8 @@
             criteria: int for test criteria.
             test_duration: int for test duration.
         """
-        begin_time = utils.get_current_epoch_time()
-        self.ad.log.info("Tests Start at %s" %
-                         utils.epoch_to_human_time(begin_time))
+        begin_time = datetime.now()
+        self.ad.log.info(f"Test Start at {begin_time}")
         self.enable_chre(criteria["gnss"])
         time.sleep(test_duration)
         self.enable_chre(0)
@@ -199,12 +240,12 @@
             self.ad.log.info("Starting process %s result" % request_type)
             outliers[request_type], failures[request_type], results[
                 request_type] = self.parse_concurrency_result(
-                    begin_time, request_type, criteria)
+                    begin_time, request_type, criteria, exam_lower=False)
             if not results[request_type]:
                 failure_log += "[%s] Fail to find location report.\n" % request_type
             if len(failures[request_type]) > 0:
-                failure_log += "[%s] Test exceeds criteria: %.2f\n" % (
-                    request_type, criteria)
+                failure_log += "[%s] Test exceeds criteria(%.2f): %.2f\n" % (
+                    request_type, criteria, max(failures[request_type]))
             if len(outliers[request_type]) > self.max_outliers:
                 failure_log += "[%s] Outliers excceds max amount: %d\n" % (
                     request_type, len(outliers[request_type]))
@@ -219,7 +260,7 @@
             freq: a list identify source1/2 frequency [freq1, freq2]
         """
         request = {"ap_location": self.max_interval}
-        begin_time = utils.get_current_epoch_time()
+        begin_time = datetime.now()
         self.ad.droid.startLocating(freq[0] * 1000, 0)
         time.sleep(10)
         for i in range(5):
@@ -253,73 +294,183 @@
         self.ad.log.info("TestResult max_position_error %.2f" %
                          max(position_errors))
 
+    def get_chre_ttff(self, interval_sec, duration):
+        """ Get the TTFF for the first CHRE report.
+
+        Args:
+            interval_sec: test interval in seconds for CHRE.
+            duration: test duration.
+        """
+        begin_time = datetime.now()
+        self.ad.log.info(f"Test start at {begin_time}")
+        self.enable_chre(interval_sec)
+        time.sleep(duration)
+        self.enable_chre(0)
+        for type, pattern in CONCURRENCY_TYPE.items():
+            if type == "ap_location":
+                continue
+            search_results = self.ad.search_logcat(pattern, begin_time)
+            if not search_results:
+                raise signals.TestFailure(
+                    f"Unable to receive {type} report in {duration} seconds")
+            else:
+                ttff_stamp = search_results[0]["datetime_obj"]
+                self.ad.log.info(search_results[0]["time_stamp"])
+                ttff = (ttff_stamp - begin_time).total_seconds()
+                self.ad.log.info(f"CHRE {type} TTFF = {ttff}")
+
+    def add_ttff_conf(self, conf_type):
+        """ Add mcu ttff config to gps.xml
+
+        Args:
+            conf_type: a string identify the config type
+        """
+        search_line_tag = "<gll\n"
+        append_line_str = GPS_XML_CONFIG[conf_type]
+        gutils.bcm_gps_xml_update_option(self.ad, "add", search_line_tag,
+                                         append_line_str)
+
+    def update_gps_conf(self, search_line, update_line):
+        """ Update gps.xml content
+
+        Args:
+            search_line: target content
+            update_line: update content
+        """
+        gutils.bcm_gps_xml_update_option(
+            self.ad, "update", search_line, update_txt=update_line)
+
+    def delete_gps_conf(self, conf_type):
+        """ Delete gps.xml content
+
+        Args:
+            conf_type: a string identify the config type
+        """
+        search_line_tag = GPS_XML_CONFIG[conf_type]
+        gutils.bcm_gps_xml_update_option(
+            self.ad, "delete", delete_txt=search_line_tag)
+
+    def preset_mcu_test(self, mode):
+        """ Preseting mcu test with config and device state
+
+        mode:
+            mode: a string identify the test type
+        """
+        self.add_ttff_conf(mode)
+        gutils.push_lhd_overlay(self.ad)
+        toggle_airplane_mode(self.ad.log, self.ad, new_state=True)
+        self.update_gps_conf(ONCHIP_CONFIG[1], ONCHIP_CONFIG[0])
+        gutils.clear_aiding_data_by_gtw_gpstool(self.ad)
+        self.ad.reboot(self.ad)
+        self.load_chre_nanoapp()
+
+    def reset_mcu_test(self, mode):
+        """ Resetting mcu test with config and device state
+
+        mode:
+            mode: a string identify the test type
+        """
+        self.delete_gps_conf(mode)
+        self.update_gps_conf(ONCHIP_CONFIG[0], ONCHIP_CONFIG[1])
+
+    def get_mcu_ttff(self):
+        """ Get mcu ttff seconds
+
+        Return:
+            ttff: a float identify ttff seconds
+        """
+        search_res = ""
+        search_pattern = "$PGLOR,0,FIX"
+        ttff_regex = r"FIX,(.*)\*"
+        cmd_base = "chre_power_test_client gnss tcm"
+        cmd_start = " ".join([cmd_base, "enable 1000"])
+        cmd_stop = " ".join([cmd_base, "disable"])
+        begin_time = datetime.now()
+
+        self.ad.log.info("Send CHRE enable to DUT")
+        self.ad.adb.shell(cmd_start)
+        for i in range(6):
+            search_res = self.ad.search_logcat(search_pattern, begin_time)
+            if search_res:
+                break
+            time.sleep(10)
+        else:
+            self.ad.adb.shell(cmd_stop)
+            self.ad.log.error("Unable to get mcu ttff in 60 seconds")
+            return 60
+        self.ad.adb.shell(cmd_stop)
+
+        res = re.search(ttff_regex, search_res[0]["log_message"])
+        ttff = res.group(1)
+        self.ad.log.info(f"TTFF = {ttff}")
+        return float(ttff)
+
+    def run_mcu_ttff_loops(self, mode, loops):
+        """ Run mcu ttff with given mode and loops
+
+        Args:
+            mode: a string identify mode cs/ws/hs.
+            loops: a int to identify the number of loops
+        """
+        ttff_res = []
+        for i in range(10):
+            ttff = self.get_mcu_ttff()
+            self.ad.log.info(f"{mode} TTFF LOOP{i+1} = {ttff}")
+            ttff_res.append(ttff)
+            time.sleep(10)
+        self.ad.log.info(f"TestResult {mode}_MAX_TTFF {max(ttff_res)}")
+        self.ad.log.info(
+            f"TestResult {mode}_AVG_TTFF {statistics.mean(ttff_res)}")
+
     # Concurrency Test Cases
-    @test_tracker_info(uuid="9b0daebf-461e-4005-9773-d5d10aaeaaa4")
-    def test_gnss_concurrency_ct1(self):
+    def test_gnss_concurrency_location_1_chre_1(self):
         test_duration = 15
         criteria = {"ap_location": 1, "gnss": 1, "gnss_meas": 1}
         self.run_gnss_concurrency_test(criteria, test_duration)
 
-    @test_tracker_info(uuid="f423db2f-12a0-4858-b66f-99e7ca6010c3")
-    def test_gnss_concurrency_ct2(self):
+    def test_gnss_concurrency_location_1_chre_8(self):
         test_duration = 30
         criteria = {"ap_location": 1, "gnss": 8, "gnss_meas": 8}
         self.run_gnss_concurrency_test(criteria, test_duration)
 
-    @test_tracker_info(uuid="f72d2df0-f70a-4a11-9f68-2a38f6974454")
-    def test_gnss_concurrency_ct3(self):
+    def test_gnss_concurrency_location_15_chre_8(self):
         test_duration = 60
         criteria = {"ap_location": 15, "gnss": 8, "gnss_meas": 8}
         self.run_gnss_concurrency_test(criteria, test_duration)
 
-    @test_tracker_info(uuid="8e5563fd-afcd-40d3-9392-7fc0d10f49da")
-    def test_gnss_concurrency_aoc1(self):
+    def test_gnss_concurrency_location_61_chre_1(self):
         test_duration = 120
         criteria = {"ap_location": 61, "gnss": 1, "gnss_meas": 1}
         self.run_gnss_concurrency_test(criteria, test_duration)
 
-    @test_tracker_info(uuid="fb258565-6ac8-4bf7-a554-01d63fc4ef54")
-    def test_gnss_concurrency_aoc2(self):
+    def test_gnss_concurrency_location_61_chre_10(self):
         test_duration = 120
         criteria = {"ap_location": 61, "gnss": 10, "gnss_meas": 10}
         self.run_gnss_concurrency_test(criteria, test_duration)
 
     # CHRE Only Test Cases
-    @test_tracker_info(uuid="cb85fa60-9f1a-4957-b5e3-0f2e5db70b47")
-    def test_gnss_chre1(self):
+    def test_gnss_chre_1(self):
         test_duration = 15
         criteria = {"gnss": 1, "gnss_meas": 1}
         self.run_chre_only_test(criteria, test_duration)
 
-    @test_tracker_info(uuid="6ab17866-0d0e-4d9e-b3af-441d9db0e324")
-    def test_gnss_chre2(self):
+    def test_gnss_chre_8(self):
         test_duration = 30
         criteria = {"gnss": 8, "gnss_meas": 8}
         self.run_chre_only_test(criteria, test_duration)
 
     # Interval tests
-    @test_tracker_info(uuid="53b161e5-335e-44a7-ae2e-eae7464a2b37")
     def test_variable_interval_via_chre(self):
         test_duration = 10
-        intervals = [{
-            "gnss": 0.1,
-            "gnss_meas": 0.1
-        }, {
-            "gnss": 0.5,
-            "gnss_meas": 0.5
-        }, {
-            "gnss": 1.5,
-            "gnss_meas": 1.5
-        }]
+        intervals = [0.1, 0.5, 1.5]
         for interval in intervals:
-            self.run_chre_only_test(interval, test_duration)
+            self.get_chre_ttff(interval, test_duration)
 
-    @test_tracker_info(uuid="ee0a46fe-aa5f-4dfd-9cb7-d4924f9e9cea")
     def test_variable_interval_via_framework(self):
         test_duration = 10
         intervals = [0, 0.5, 1.5]
         for interval in intervals:
-            begin_time = utils.get_current_epoch_time()
+            begin_time = datetime.now()
             self.ad.droid.startLocating(interval * 1000, 0)
             time.sleep(test_duration)
             self.ad.droid.stopLocating()
@@ -327,14 +478,30 @@
             self.parse_concurrency_result(begin_time, "ap_location", criteria)
 
     # Engine switching test
-    @test_tracker_info(uuid="8b42bcb2-cb8c-4ef9-bd98-4fb74a521224")
     def test_gps_engine_switching_host_to_onchip(self):
         self.is_brcm_test()
         freq = [1, self.onchip_interval]
         self.run_engine_switching_test(freq)
 
-    @test_tracker_info(uuid="636041dc-2bd6-4854-aa5d-61c87943d99c")
     def test_gps_engine_switching_onchip_to_host(self):
         self.is_brcm_test()
         freq = [self.onchip_interval, 1]
         self.run_engine_switching_test(freq)
+
+    def test_mcu_cs_ttff(self):
+        mode = "CS"
+        self.preset_mcu_test(mode)
+        self.run_mcu_ttff_loops(mode, self.ttff_test_cycle)
+        self.reset_mcu_test(mode)
+
+    def test_mcu_ws_ttff(self):
+        mode = "WS"
+        self.preset_mcu_test(mode)
+        self.run_mcu_ttff_loops(mode, self.ttff_test_cycle)
+        self.reset_mcu_test(mode)
+
+    def test_mcu_hs_ttff(self):
+        mode = "HS"
+        self.preset_mcu_test(mode)
+        self.run_mcu_ttff_loops(mode, self.ttff_test_cycle)
+        self.reset_mcu_test(mode)
diff --git a/acts_tests/tests/google/gnss/GnssFunctionTest.py b/acts_tests/tests/google/gnss/GnssFunctionTest.py
index d45a997..b22cd4f 100644
--- a/acts_tests/tests/google/gnss/GnssFunctionTest.py
+++ b/acts_tests/tests/google/gnss/GnssFunctionTest.py
@@ -13,18 +13,14 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-import time
+
 import os
 import re
 import fnmatch
-from multiprocessing import Process
 
 from acts import asserts
 from acts import signals
 from acts.base_test import BaseTestClass
-from acts.test_decorators import test_tracker_info
-from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
-from acts_contrib.test_utils.tel import tel_test_utils as tutils
 from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
 from acts.utils import get_current_epoch_time
 from acts.utils import unzip_maintain_permissions
@@ -32,9 +28,8 @@
 from acts_contrib.test_utils.tel.tel_bootloader_utils import flash_radio
 from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
 from acts_contrib.test_utils.tel.tel_test_utils import check_call_state_connected_by_adb
-from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
-from acts_contrib.test_utils.tel.tel_data_utils import http_file_download_by_sl4a
 from acts_contrib.test_utils.gnss.gnss_test_utils import get_baseband_and_gms_version
 from acts_contrib.test_utils.gnss.gnss_test_utils import set_attenuator_gnss_signal
 from acts_contrib.test_utils.gnss.gnss_test_utils import _init_device
@@ -49,28 +44,18 @@
 from acts_contrib.test_utils.gnss.gnss_test_utils import launch_google_map
 from acts_contrib.test_utils.gnss.gnss_test_utils import check_location_api
 from acts_contrib.test_utils.gnss.gnss_test_utils import set_battery_saver_mode
-from acts_contrib.test_utils.gnss.gnss_test_utils import kill_xtra_daemon
 from acts_contrib.test_utils.gnss.gnss_test_utils import start_gnss_by_gtw_gpstool
 from acts_contrib.test_utils.gnss.gnss_test_utils import process_gnss_by_gtw_gpstool
-from acts_contrib.test_utils.gnss.gnss_test_utils import start_ttff_by_gtw_gpstool
-from acts_contrib.test_utils.gnss.gnss_test_utils import process_ttff_by_gtw_gpstool
-from acts_contrib.test_utils.gnss.gnss_test_utils import check_ttff_data
-from acts_contrib.test_utils.gnss.gnss_test_utils import start_youtube_video
-from acts_contrib.test_utils.gnss.gnss_test_utils import fastboot_factory_reset
-from acts_contrib.test_utils.gnss.gnss_test_utils import gnss_trigger_modem_ssr_by_mds
-from acts_contrib.test_utils.gnss.gnss_test_utils import disable_supl_mode
 from acts_contrib.test_utils.gnss.gnss_test_utils import connect_to_wifi_network
-from acts_contrib.test_utils.gnss.gnss_test_utils import check_xtra_download
 from acts_contrib.test_utils.gnss.gnss_test_utils import gnss_tracking_via_gtw_gpstool
 from acts_contrib.test_utils.gnss.gnss_test_utils import parse_gtw_gpstool_log
-from acts_contrib.test_utils.gnss.gnss_test_utils import enable_supl_mode
 from acts_contrib.test_utils.gnss.gnss_test_utils import start_toggle_gnss_by_gtw_gpstool
 from acts_contrib.test_utils.gnss.gnss_test_utils import grant_location_permission
 from acts_contrib.test_utils.gnss.gnss_test_utils import is_mobile_data_on
 from acts_contrib.test_utils.gnss.gnss_test_utils import is_wearable_btwifi
-from acts_contrib.test_utils.gnss.gnss_test_utils import delete_lto_file
 from acts_contrib.test_utils.gnss.gnss_test_utils import is_device_wearable
-from acts_contrib.test_utils.tel.tel_logging_utils import start_adb_tcpdump
+from acts_contrib.test_utils.gnss.gnss_test_utils import log_current_epoch_time
+from acts_contrib.test_utils.gnss.testtracker_util import log_testtracker_uuid
 from acts_contrib.test_utils.tel.tel_logging_utils import stop_adb_tcpdump
 from acts_contrib.test_utils.tel.tel_logging_utils import get_tcpdump_log
 
@@ -80,25 +65,20 @@
     def setup_class(self):
         super().setup_class()
         self.ad = self.android_devices[0]
-        req_params = ["pixel_lab_network", "standalone_cs_criteria",
+        req_params = ["pixel_lab_network",
                       "standalone_ws_criteria", "standalone_hs_criteria",
-                      "supl_cs_criteria", "supl_ws_criteria",
-                      "supl_hs_criteria", "xtra_cs_criteria",
-                      "xtra_ws_criteria", "xtra_hs_criteria",
-                      "weak_signal_supl_cs_criteria",
-                      "weak_signal_supl_ws_criteria",
-                      "weak_signal_supl_hs_criteria",
-                      "weak_signal_xtra_cs_criteria",
-                      "weak_signal_xtra_ws_criteria",
-                      "weak_signal_xtra_hs_criteria",
+                      "supl_cs_criteria",
+                      "supl_hs_criteria",
+                      "standalone_cs_criteria",
                       "wearable_reboot_hs_criteria",
                       "default_gnss_signal_attenuation",
                       "weak_gnss_signal_attenuation",
-                      "no_gnss_signal_attenuation", "gnss_init_error_list",
+                      "gnss_init_error_list",
                       "gnss_init_error_allowlist", "pixel_lab_location",
-                      "qdsp6m_path", "supl_capabilities", "ttff_test_cycle",
+                      "qdsp6m_path", "ttff_test_cycle",
                       "collect_logs", "dpo_threshold",
-                      "brcm_error_log_allowlist"]
+                      "brcm_error_log_allowlist", "onchip_interval", "adr_ratio_threshold",
+                      "set_attenuator", "weak_signal_criteria", "weak_signal_cs_criteria"]
         self.unpack_userparams(req_param_names=req_params)
         # create hashmap for SSID
         self.ssid_map = {}
@@ -109,23 +89,35 @@
                           "ws": "Warm Start",
                           "hs": "Hot Start",
                           "csa": "CSWith Assist"}
-        if self.collect_logs and \
-            gutils.check_chipset_vendor_by_qualcomm(self.ad):
+        if self.collect_logs and gutils.check_chipset_vendor_by_qualcomm(self.ad):
             self.flash_new_radio_or_mbn()
             self.push_gnss_cfg()
-        _init_device(self.ad)
+        self.init_device()
+
+    def init_device(self):
+        gutils._init_device(self.ad)
+        gutils.enable_supl_mode(self.ad)
+        gutils.enable_vendor_orbit_assistance_data(self.ad)
+        gutils.disable_ramdump(self.ad)
 
     def setup_test(self):
+        log_current_epoch_time(self.ad, "test_start_time")
+        log_testtracker_uuid(self.ad, self.current_test_name)
         get_baseband_and_gms_version(self.ad)
         if self.collect_logs:
             clear_logd_gnss_qxdm_log(self.ad)
+        if self.set_attenuator:
             set_attenuator_gnss_signal(self.ad, self.attenuators,
                                        self.default_gnss_signal_attenuation)
         # TODO (b/202101058:chenstanley): Need to double check how to disable wifi successfully in wearable projects.
         if is_wearable_btwifi(self.ad):
             wifi_toggle_state(self.ad, True)
             connect_to_wifi_network(
-            self.ad, self.ssid_map[self.pixel_lab_network[0]["SSID"]])
+                self.ad, self.ssid_map[self.pixel_lab_network[0]["SSID"]])
+        else:
+            wifi_toggle_state(self.ad, False)
+            set_mobile_data(self.ad, True)
+
         if not verify_internet_connection(self.ad.log, self.ad, retries=3,
                                           expected_state=True):
             raise signals.TestFailure("Fail to connect to LTE network.")
@@ -134,6 +126,7 @@
         if self.collect_logs:
             gutils.stop_pixel_logger(self.ad)
             stop_adb_tcpdump(self.ad)
+        if self.set_attenuator:
             set_attenuator_gnss_signal(self.ad, self.attenuators,
                                        self.default_gnss_signal_attenuation)
         # TODO(chenstanley): sim structure issue
@@ -142,17 +135,11 @@
                 hangup_call(self.ad.log, self.ad)
         if self.ad.droid.connectivityCheckAirplaneMode():
             self.ad.log.info("Force airplane mode off")
-            self.ad.droid.connectivityToggleAirplaneMode(False)
-        if not is_wearable_btwifi and self.ad.droid.wifiCheckState():
-            wifi_toggle_state(self.ad, False)
-        if not is_mobile_data_on(self.ad):
-            set_mobile_data(self.ad, True)
+            toggle_airplane_mode(self.ad.log, self.ad, new_state=False)
         if int(self.ad.adb.shell(
             "settings get global wifi_scan_always_enabled")) != 1:
             set_wifi_and_bt_scanning(self.ad, True)
-        if not verify_internet_connection(self.ad.log, self.ad, retries=3,
-                                          expected_state=True):
-            raise signals.TestFailure("Fail to connect to LTE network.")
+        log_current_epoch_time(self.ad, "test_end_time")
 
     def on_fail(self, test_name, begin_time):
         if self.collect_logs:
@@ -248,41 +235,6 @@
                 self.ad.log.error("cat mcfg.version with error %s", e)
                 return False
 
-    def run_ttff_via_gtw_gpstool(self, mode, criteria):
-        """Run GNSS TTFF test with selected mode and parse the results.
-
-        Args:
-            mode: "cs", "ws" or "hs"
-            criteria: Criteria for the TTFF.
-        """
-        begin_time = get_current_epoch_time()
-        process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
-        start_ttff_by_gtw_gpstool(self.ad, mode, self.ttff_test_cycle)
-        ttff_data = process_ttff_by_gtw_gpstool(
-            self.ad, begin_time, self.pixel_lab_location)
-        result = check_ttff_data(
-            self.ad, ttff_data, self.ttff_mode.get(mode), criteria)
-        asserts.assert_true(
-            result, "TTFF %s fails to reach designated criteria of %d "
-                    "seconds." % (self.ttff_mode.get(mode), criteria))
-
-    def start_qxdm_and_tcpdump_log(self):
-        """Start QXDM and adb tcpdump if collect_logs is True."""
-        if self.collect_logs:
-            gutils.start_pixel_logger(self.ad)
-            start_adb_tcpdump(self.ad)
-
-    def supl_ttff_with_sim(self, mode, criteria):
-        """Verify SUPL TTFF functionality.
-
-        Args:
-            mode: "cs", "ws" or "hs"
-            criteria: Criteria for the test.
-        """
-        kill_xtra_daemon(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        self.run_ttff_via_gtw_gpstool(mode, criteria)
-
     def standalone_ttff_airplane_mode_on(self, mode, criteria):
         """Verify Standalone GNSS TTFF functionality while airplane mode is on.
 
@@ -290,92 +242,79 @@
             mode: "cs", "ws" or "hs"
             criteria: Criteria for the test.
         """
-        self.start_qxdm_and_tcpdump_log()
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
         self.ad.log.info("Turn airplane mode on")
-        self.ad.droid.connectivityToggleAirplaneMode(True)
-        self.run_ttff_via_gtw_gpstool(mode, criteria)
-
-    def supl_ttff_weak_gnss_signal(self, mode, criteria):
-        """Verify SUPL TTFF functionality under weak GNSS signal.
-
-        Args:
-            mode: "cs", "ws" or "hs"
-            criteria: Criteria for the test.
-        """
-        set_attenuator_gnss_signal(self.ad, self.attenuators,
-                                   self.weak_gnss_signal_attenuation)
-        kill_xtra_daemon(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        self.run_ttff_via_gtw_gpstool(mode, criteria)
-
-    def xtra_ttff_mobile_data(self, mode, criteria):
-        """Verify XTRA\LTO TTFF functionality with mobile data.
-
-        Args:
-            mode: "cs", "ws" or "hs"
-            criteria: Criteria for the test.
-        """
-        disable_supl_mode(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        self.run_ttff_via_gtw_gpstool(mode, criteria)
-
-    def xtra_ttff_weak_gnss_signal(self, mode, criteria):
-        """Verify XTRA\LTO TTFF functionality under weak GNSS signal.
-
-        Args:
-            mode: "cs", "ws" or "hs"
-            criteria: Criteria for the test.
-        """
-        set_attenuator_gnss_signal(self.ad, self.attenuators,
-                                   self.weak_gnss_signal_attenuation)
-        disable_supl_mode(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        self.run_ttff_via_gtw_gpstool(mode, criteria)
-
-    def xtra_ttff_wifi(self, mode, criteria):
-        """Verify XTRA\LTO TTFF functionality with WiFi.
-
-        Args:
-            mode: "cs", "ws" or "hs"
-            criteria: Criteria for the test.
-        """
-        disable_supl_mode(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        self.ad.log.info("Turn airplane mode on")
-        self.ad.droid.connectivityToggleAirplaneMode(True)
-        wifi_toggle_state(self.ad, True)
-        connect_to_wifi_network(
-            self.ad, self.ssid_map[self.pixel_lab_network[0]["SSID"]])
-        self.run_ttff_via_gtw_gpstool(mode, criteria)
-
-    def ttff_with_assist(self, mode, criteria):
-        """Verify CS/WS TTFF functionality with Assist data.
-
-        Args:
-            mode: "csa" or "ws"
-            criteria: Criteria for the test.
-        """
-        disable_supl_mode(self.ad)
-        begin_time = get_current_epoch_time()
-        process_gnss_by_gtw_gpstool(
-            self.ad, self.standalone_cs_criteria)
-        check_xtra_download(self.ad, begin_time)
-        self.ad.log.info("Turn airplane mode on")
-        self.ad.droid.connectivityToggleAirplaneMode(True)
-        start_gnss_by_gtw_gpstool(self.ad, True)
-        start_ttff_by_gtw_gpstool(
-            self.ad, mode, iteration=self.ttff_test_cycle)
-        ttff_data = process_ttff_by_gtw_gpstool(
-            self.ad, begin_time, self.pixel_lab_location)
-        result = check_ttff_data(
-            self.ad, ttff_data, mode, criteria)
-        asserts.assert_true(
-            result, "TTFF %s fails to reach designated criteria of %d "
-                    "seconds." % (self.ttff_mode.get(mode), criteria))
+        toggle_airplane_mode(self.ad.log, self.ad, new_state=True)
+        gutils.run_ttff_via_gtw_gpstool(
+            self.ad, mode, criteria, self.ttff_test_cycle, self.pixel_lab_location)
 
     """ Test Cases """
 
-    @test_tracker_info(uuid="ab859f2a-2c95-4d15-bb7f-bd0e3278340f")
+    def test_cs_first_fixed_system_server_restart(self):
+        """Verify cs first fixed after system server restart.
+
+        Steps:
+            1. Get location fixed within supl_cs_criteria.
+            2. Restarts android runtime.
+            3. Get location fixed within supl_cs_criteria.
+
+        Expected Results:
+            Location fixed within supl_cs_criteria.
+        """
+        overall_test_result = []
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
+        for test_loop in range(1, 6):
+            gutils.process_gnss_by_gtw_gpstool(self.ad, self.supl_cs_criteria)
+            gutils.start_gnss_by_gtw_gpstool(self.ad, False)
+            self.ad.restart_runtime()
+            self.ad.unlock_screen(password=None)
+            test_result = gutils.process_gnss_by_gtw_gpstool(self.ad, self.supl_cs_criteria)
+            gutils.start_gnss_by_gtw_gpstool(self.ad, False)
+            self.ad.log.info("Iteration %d => %s" % (test_loop, test_result))
+            overall_test_result.append(test_result)
+
+        asserts.assert_true(all(overall_test_result),
+                            "SUPL fail after system server restart.")
+
+    def test_cs_ttff_after_gps_service_restart(self):
+        """Verify cs ttff after modem silent reboot / GPS daemons restart.
+
+        Steps:
+            1. Trigger modem crash by adb/Restart GPS daemons by killing PID.
+            2. Wait 1 minute for modem to recover.
+            3. TTFF Cold Start for 3 iteration.
+            4. Repeat Step 1. to Step 3. for 5 times.
+
+        Expected Results:
+            All SUPL TTFF Cold Start results should be within supl_cs_criteria.
+        """
+        supl_ssr_test_result_all = []
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
+        for times in range(1, 6):
+            begin_time = get_current_epoch_time()
+            if gutils.check_chipset_vendor_by_qualcomm(self.ad):
+                test_info = "Modem SSR"
+                gutils.gnss_trigger_modem_ssr_by_mds(self.ad)
+            else:
+                test_info = "restarting GPS daemons"
+                gutils.restart_gps_daemons(self.ad)
+            if not verify_internet_connection(self.ad.log, self.ad, retries=3,
+                                                expected_state=True):
+                raise signals.TestFailure("Fail to connect to LTE network.")
+            gutils.process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
+            gutils.start_ttff_by_gtw_gpstool(self.ad, ttff_mode="cs", iteration=3)
+            ttff_data = gutils.process_ttff_by_gtw_gpstool(self.ad, begin_time,
+                                                    self.pixel_lab_location)
+            supl_ssr_test_result = gutils.check_ttff_data(
+                self.ad, ttff_data, ttff_mode="Cold Start",
+                criteria=self.supl_cs_criteria)
+            self.ad.log.info("SUPL after %s test %d times -> %s" % (
+                test_info, times, supl_ssr_test_result))
+            supl_ssr_test_result_all.append(supl_ssr_test_result)
+
+        asserts.assert_true(all(supl_ssr_test_result_all),
+                            "TTFF fails to reach designated criteria")
+
     def test_gnss_one_hour_tracking(self):
         """Verify GNSS tracking performance of signal strength and position
         error.
@@ -387,14 +326,17 @@
         Expected Results:
             DUT could finish 60 minutes test and output track data.
         """
-        self.start_qxdm_and_tcpdump_log()
+        test_time = 60
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
         gnss_tracking_via_gtw_gpstool(self.ad, self.standalone_cs_criteria,
-                                      type="gnss", testtime=60)
-        parse_gtw_gpstool_log(self.ad, self.pixel_lab_location, type="gnss")
+                                      api_type="gnss", testtime=test_time)
+        location_data = parse_gtw_gpstool_log(self.ad, self.pixel_lab_location, api_type="gnss")
+        gutils.validate_location_fix_rate(self.ad, location_data, run_time=test_time,
+                                          fix_rate_criteria=0.99)
+        gutils.verify_gps_time_should_be_close_to_device_time(self.ad, location_data)
 
-    @test_tracker_info(uuid="623628ab-fdab-449d-9025-ebf4e9a404c2")
-    def test_dpo_function(self):
-        """Verify DPO Functionality.
+    def test_duty_cycle_function(self):
+        """Verify duty cycle Functionality.
 
         Steps:
             1. Launch GTW_GPSTool.
@@ -403,14 +345,14 @@
             4. Calculate the count diff of "HardwareClockDiscontinuityCount"
 
         Expected Results:
-            DPO should be engaged in 5 minutes GNSS tracking.
+            Duty cycle should be engaged in 5 minutes GNSS tracking.
         """
         tracking_minutes = 5
-        self.start_qxdm_and_tcpdump_log()
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
         dpo_begin_time = get_current_epoch_time()
         gnss_tracking_via_gtw_gpstool(self.ad,
                                       self.standalone_cs_criteria,
-                                      type="gnss",
+                                      api_type="gnss",
                                       testtime=tracking_minutes,
                                       meas_flag=True)
         if gutils.check_chipset_vendor_by_qualcomm(self.ad):
@@ -422,7 +364,6 @@
                                                self.dpo_threshold,
                                                self.brcm_error_log_allowlist)
 
-    @test_tracker_info(uuid="499d2091-640a-4735-9c58-de67370e4421")
     def test_gnss_init_error(self):
         """Check if there is any GNSS initialization error after reboot.
 
@@ -452,30 +393,6 @@
         asserts.assert_true(error_mismatch, "Error message found after GNSS "
                                             "init")
 
-    @test_tracker_info(uuid="ff318483-411c-411a-8b1a-422bd54f4a3f")
-    def test_supl_capabilities(self):
-        """Verify SUPL capabilities.
-
-        Steps:
-            1. Root DUT.
-            2. Check SUPL capabilities.
-
-        Expected Results:
-            CAPABILITIES=0x37 which supports MSA + MSB.
-            CAPABILITIES=0x17 = ON_DEMAND_TIME | MSA | MSB | SCHEDULING
-        """
-        if not gutils.check_chipset_vendor_by_qualcomm(self.ad):
-            raise signals.TestSkip("Not Qualcomm chipset. Skip the test.")
-        capabilities_state = str(
-            self.ad.adb.shell(
-                "cat vendor/etc/gps.conf | grep CAPABILITIES")).split("=")[-1]
-        self.ad.log.info("SUPL capabilities - %s" % capabilities_state)
-        asserts.assert_true(capabilities_state in self.supl_capabilities,
-                            "Wrong default SUPL capabilities is set. Found %s, "
-                            "expected any of %r" % (capabilities_state,
-                                                    self.supl_capabilities))
-
-    @test_tracker_info(uuid="dcae6979-ddb4-4cad-9d14-fbdd9439cf42")
     def test_sap_valid_modes(self):
         """Verify SAP Valid Modes.
 
@@ -494,7 +411,6 @@
         asserts.assert_true("SAP=PREMIUM" in sap_state,
                             "Wrong SAP Valid Modes is set")
 
-    @test_tracker_info(uuid="14daaaba-35b4-42d9-8d2c-2a803dd746a6")
     def test_network_location_provider_cell(self):
         """Verify LocationManagerService API reports cell Network Location.
 
@@ -508,7 +424,7 @@
             Test devices could report cell Network Location.
         """
         test_result_all = []
-        self.start_qxdm_and_tcpdump_log()
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
         set_wifi_and_bt_scanning(self.ad, False)
         for i in range(1, 6):
             test_result = check_network_location(
@@ -519,7 +435,6 @@
         asserts.assert_true(all(test_result_all),
                             "Fail to get networkLocationType=cell")
 
-    @test_tracker_info(uuid="a45bdc7d-29fa-4a1d-ba34-6340b90e308d")
     def test_network_location_provider_wifi(self):
         """Verify LocationManagerService API reports wifi Network Location.
 
@@ -533,7 +448,7 @@
             Test devices could report wifi Network Location.
         """
         test_result_all = []
-        self.start_qxdm_and_tcpdump_log()
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
         set_wifi_and_bt_scanning(self.ad, True)
         for i in range(1, 6):
             test_result = check_network_location(
@@ -543,62 +458,6 @@
         asserts.assert_true(all(test_result_all),
                             "Fail to get networkLocationType=wifi")
 
-    @test_tracker_info(uuid="0919d375-baf2-4fe7-b66b-3f72d386f791")
-    def test_gmap_location_report_gps_network(self):
-        """Verify GnssLocationProvider API reports location to Google Map
-           when GPS and Location Accuracy are on.
-
-        Steps:
-            1. GPS and NLP are on.
-            2. Launch Google Map.
-            3. Verify whether test devices could report location.
-            4. Repeat Step 2. to Step 3. for 5 times.
-
-        Expected Results:
-            Test devices could report location to Google Map.
-        """
-        test_result_all = []
-        self.start_qxdm_and_tcpdump_log()
-        for i in range(1, 6):
-            grant_location_permission(self.ad, True)
-            launch_google_map(self.ad)
-            test_result = check_location_api(self.ad, retries=3)
-            self.ad.send_keycode("HOME")
-            test_result_all.append(test_result)
-            self.ad.log.info("Iteration %d => %s" % (i, test_result))
-        asserts.assert_true(all(test_result_all), "Fail to get location update")
-
-    @test_tracker_info(uuid="513361d2-7d72-41b0-a944-fb259c606b81")
-    def test_gmap_location_report_gps(self):
-        """Verify GnssLocationProvider API reports location to Google Map
-           when GPS is on and Location Accuracy is off.
-
-        Steps:
-            1. GPS is on.
-            2. Location Accuracy is off.
-            3. Launch Google Map.
-            4. Verify whether test devices could report location.
-            5. Repeat Step 3. to Step 4. for 5 times.
-
-        Expected Results:
-            Test devices could report location to Google Map.
-        """
-        test_result_all = []
-        self.start_qxdm_and_tcpdump_log()
-        self.ad.adb.shell("settings put secure location_mode 1")
-        out = int(self.ad.adb.shell("settings get secure location_mode"))
-        self.ad.log.info("Modify current Location Mode to %d" % out)
-        for i in range(1, 6):
-            grant_location_permission(self.ad, True)
-            launch_google_map(self.ad)
-            test_result = check_location_api(self.ad, retries=3)
-            self.ad.send_keycode("HOME")
-            test_result_all.append(test_result)
-            self.ad.log.info("Iteration %d => %s" % (i, test_result))
-        check_location_service(self.ad)
-        asserts.assert_true(all(test_result_all), "Fail to get location update")
-
-    @test_tracker_info(uuid="91a65121-b87d-450d-bd0f-387ade450ab7")
     def test_gmap_location_report_battery_saver(self):
         """Verify GnssLocationProvider API reports location to Google Map
            when Battery Saver is enabled.
@@ -615,7 +474,7 @@
             Test devices could report location to Google Map.
         """
         test_result_all = []
-        self.start_qxdm_and_tcpdump_log()
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
         set_battery_saver_mode(self.ad, True)
         for i in range(1, 6):
             grant_location_permission(self.ad, True)
@@ -627,174 +486,6 @@
         set_battery_saver_mode(self.ad, False)
         asserts.assert_true(all(test_result_all), "Fail to get location update")
 
-    @test_tracker_info(uuid="a59c72af-5d56-4d88-9746-ae2749cac671")
-    def test_supl_ttff_cs(self):
-        """Verify SUPL functionality of TTFF Cold Start.
-
-        Steps:
-            1. Kill XTRA/LTO daemon to support SUPL only case.
-            2. SUPL TTFF Cold Start for 10 iteration.
-
-        Expected Results:
-            All SUPL TTFF Cold Start results should be less than
-            supl_cs_criteria.
-        """
-        self.supl_ttff_with_sim("cs", self.supl_cs_criteria)
-
-    @test_tracker_info(uuid="9a91c8ad-1978-414a-a9ac-8ebc782f77ff")
-    def test_supl_ttff_ws(self):
-        """Verify SUPL functionality of TTFF Warm Start.
-
-        Steps:
-            1. Kill XTRA/LTO daemon to support SUPL only case.
-            2. SUPL TTFF Warm Start for 10 iteration.
-
-        Expected Results:
-            All SUPL TTFF Warm Start results should be less than
-            supl_ws_criteria.
-        """
-        self.supl_ttff_with_sim("ws", self.supl_ws_criteria)
-
-    @test_tracker_info(uuid="bbd5aad4-3309-4579-a3b2-a06bfb674dfa")
-    def test_supl_ttff_hs(self):
-        """Verify SUPL functionality of TTFF Hot Start.
-
-        Steps:
-            1. Kill XTRA/LTO daemon to support SUPL only case.
-            2. SUPL TTFF Hot Start for 10 iteration.
-
-        Expected Results:
-            All SUPL TTFF Hot Start results should be less than
-            supl_hs_criteria.
-        """
-        self.supl_ttff_with_sim("hs", self.supl_hs_criteria)
-
-    @test_tracker_info(uuid="60c0aeec-0c8f-4a96-bc6c-05cba1260e73")
-    def test_supl_ongoing_call(self):
-        """Verify SUPL functionality during phone call.
-
-        Steps:
-            1. Kill XTRA/LTO daemon to support SUPL only case.
-            2. Initiate call on DUT.
-            3. SUPL TTFF Cold Start for 10 iteration.
-            4. DUT hang up call.
-
-        Expected Results:
-            All SUPL TTFF Cold Start results should be less than
-            supl_cs_criteria.
-        """
-        kill_xtra_daemon(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        self.ad.droid.setVoiceCallVolume(25)
-        initiate_call(self.ad.log, self.ad, "99117")
-        time.sleep(5)
-        if not check_call_state_connected_by_adb(self.ad):
-            raise signals.TestFailure("Call is not connected.")
-        self.run_ttff_via_gtw_gpstool("cs", self.supl_cs_criteria)
-
-    @test_tracker_info(uuid="df605509-328f-43e8-b6d8-00635bf701ef")
-    def test_supl_downloading_files(self):
-        """Verify SUPL functionality when downloading files.
-
-        Steps:
-            1. Kill XTRA/LTO daemon to support SUPL only case.
-            2. DUT start downloading files by sl4a.
-            3. SUPL TTFF Cold Start for 10 iteration.
-            4. DUT cancel downloading files.
-
-        Expected Results:
-            All SUPL TTFF Cold Start results should be within supl_cs_criteria.
-        """
-        begin_time = get_current_epoch_time()
-        kill_xtra_daemon(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        download = Process(target=http_file_download_by_sl4a,
-                           args=(self.ad, "https://speed.hetzner.de/10GB.bin",
-                                 None, None, True, 3600))
-        download.start()
-        time.sleep(10)
-        process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
-        start_ttff_by_gtw_gpstool(
-            self.ad, ttff_mode="cs", iteration=self.ttff_test_cycle)
-        ttff_data = process_ttff_by_gtw_gpstool(self.ad, begin_time,
-                                                self.pixel_lab_location)
-        download.terminate()
-        time.sleep(3)
-        result = check_ttff_data(self.ad, ttff_data, ttff_mode="Cold Start",
-                                 criteria=self.supl_cs_criteria)
-        asserts.assert_true(result, "TTFF fails to reach designated criteria")
-
-    @test_tracker_info(uuid="66b9f9d4-1397-4da7-9e55-8b89b1732017")
-    def test_supl_watching_youtube(self):
-        """Verify SUPL functionality when watching video on youtube.
-
-        Steps:
-            1. Kill XTRA/LTO daemon to support SUPL only case.
-            2. DUT start watching video on youtube.
-            3. SUPL TTFF Cold Start for 10 iteration at the background.
-            4. DUT stop watching video on youtube.
-
-        Expected Results:
-            All SUPL TTFF Cold Start results should be within supl_cs_criteria.
-        """
-        begin_time = get_current_epoch_time()
-        kill_xtra_daemon(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        self.ad.droid.setMediaVolume(25)
-        process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
-        start_ttff_by_gtw_gpstool(
-            self.ad, ttff_mode="cs", iteration=self.ttff_test_cycle)
-        start_youtube_video(self.ad,
-                            url="https://www.youtube.com/watch?v=AbdVsi1VjQY",
-                            retries=3)
-        ttff_data = process_ttff_by_gtw_gpstool(self.ad, begin_time,
-                                                self.pixel_lab_location)
-        result = check_ttff_data(self.ad, ttff_data, ttff_mode="Cold Start",
-                                 criteria=self.supl_cs_criteria)
-        asserts.assert_true(result, "TTFF fails to reach designated criteria")
-
-    @test_tracker_info(uuid="a748af8b-e1eb-4ec6-bde3-74bcefa1c680")
-    def test_supl_modem_ssr(self):
-        """Verify SUPL functionality after modem silent reboot /
-        GPS daemons restart.
-
-        Steps:
-            1. Trigger modem crash by adb/Restart GPS daemons by killing PID.
-            2. Wait 1 minute for modem to recover.
-            3. SUPL TTFF Cold Start for 3 iteration.
-            4. Repeat Step 1. to Step 3. for 5 times.
-
-        Expected Results:
-            All SUPL TTFF Cold Start results should be within supl_cs_criteria.
-        """
-        supl_ssr_test_result_all = []
-        kill_xtra_daemon(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        for times in range(1, 6):
-            begin_time = get_current_epoch_time()
-            if gutils.check_chipset_vendor_by_qualcomm(self.ad):
-                test_info = "Modem SSR"
-                gnss_trigger_modem_ssr_by_mds(self.ad)
-            else:
-                test_info = "restarting GPS daemons"
-                gutils.restart_gps_daemons(self.ad)
-            if not verify_internet_connection(self.ad.log, self.ad, retries=3,
-                                              expected_state=True):
-                raise signals.TestFailure("Fail to connect to LTE network.")
-            process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
-            start_ttff_by_gtw_gpstool(self.ad, ttff_mode="cs", iteration=3)
-            ttff_data = process_ttff_by_gtw_gpstool(self.ad, begin_time,
-                                                    self.pixel_lab_location)
-            supl_ssr_test_result = check_ttff_data(
-                self.ad, ttff_data, ttff_mode="Cold Start",
-                criteria=self.supl_cs_criteria)
-            self.ad.log.info("SUPL after %s test %d times -> %s" % (
-                test_info, times, supl_ssr_test_result))
-            supl_ssr_test_result_all.append(supl_ssr_test_result)
-        asserts.assert_true(all(supl_ssr_test_result_all),
-                            "TTFF fails to reach designated criteria")
-
-    @test_tracker_info(uuid="01602e65-8ded-4459-8df1-7df70a1bfe8a")
     def test_gnss_ttff_cs_airplane_mode_on(self):
         """Verify Standalone GNSS functionality of TTFF Cold Start while
         airplane mode is on.
@@ -809,7 +500,6 @@
         """
         self.standalone_ttff_airplane_mode_on("cs", self.standalone_cs_criteria)
 
-    @test_tracker_info(uuid="30b9e7c2-0048-4ccd-b3ae-f385eb5f4e46")
     def test_gnss_ttff_ws_airplane_mode_on(self):
         """Verify Standalone GNSS functionality of TTFF Warm Start while
         airplane mode is on.
@@ -824,7 +514,6 @@
         """
         self.standalone_ttff_airplane_mode_on("ws", self.standalone_ws_criteria)
 
-    @test_tracker_info(uuid="8f3c323a-c625-4339-ab7a-6a41d34cba8f")
     def test_gnss_ttff_hs_airplane_mode_on(self):
         """Verify Standalone GNSS functionality of TTFF Hot Start while
         airplane mode is on.
@@ -839,372 +528,54 @@
         """
         self.standalone_ttff_airplane_mode_on("hs", self.standalone_hs_criteria)
 
-    @test_tracker_info(uuid="23731b0d-cb80-4c79-a877-cfe7c2faa447")
-    def test_gnss_mobile_data_off(self):
-        """Verify Standalone GNSS functionality while mobile radio is off.
-
-        Steps:
-            1. Disable mobile data.
-            2. TTFF Cold Start for 10 iteration.
-            3. Enable mobile data.
-
-        Expected Results:
-            All Standalone TTFF Cold Start results should be within
-            standalone_cs_criteria.
-        """
-        kill_xtra_daemon(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        set_mobile_data(self.ad, False)
-        self.run_ttff_via_gtw_gpstool("cs", self.standalone_cs_criteria)
-
-    @test_tracker_info(uuid="085b86a9-0212-4c0f-8ca1-2e467a0a2e6e")
-    def test_supl_after_regain_gnss_signal(self):
-        """Verify SUPL functionality after regain GNSS signal.
-
-        Steps:
-            1. Get location fixed.
-            2  Let device do GNSS tracking for 1 minute.
-            3. Set attenuation value to block GNSS signal.
-            4. Let DUT stay in no GNSS signal for 5 minutes.
-            5. Set attenuation value to regain GNSS signal.
-            6. Try to get location reported again.
-            7. Repeat Step 1. to Step 6. for 5 times.
-
-        Expected Results:
-            After setting attenuation value to 10 (GPS signal regain),
-            DUT could get location fixed again.
-        """
-        supl_no_gnss_signal_all = []
-        enable_supl_mode(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        for times in range(1, 6):
-            process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
-            self.ad.log.info("Let device do GNSS tracking for 1 minute.")
-            time.sleep(60)
-            set_attenuator_gnss_signal(self.ad, self.attenuators,
-                                       self.no_gnss_signal_attenuation)
-            self.ad.log.info("Let device stay in no GNSS signal for 5 minutes.")
-            time.sleep(300)
-            set_attenuator_gnss_signal(self.ad, self.attenuators,
-                                       self.default_gnss_signal_attenuation)
-            supl_no_gnss_signal = check_location_api(self.ad, retries=3)
-            start_gnss_by_gtw_gpstool(self.ad, False)
-            self.ad.log.info("SUPL without GNSS signal test %d times -> %s"
-                             % (times, supl_no_gnss_signal))
-            supl_no_gnss_signal_all.append(supl_no_gnss_signal)
-        asserts.assert_true(all(supl_no_gnss_signal_all),
-                            "Fail to get location update")
-
-    @test_tracker_info(uuid="3ff2f2fa-42d8-47fa-91de-060816cca9df")
-    def test_supl_ttff_cs_weak_gnss_signal(self):
-        """Verify SUPL functionality of TTFF Cold Start under weak GNSS signal.
+    def test_cs_ttff_in_weak_gnss_signal(self):
+        """Verify TTFF cold start under weak GNSS signal.
 
         Steps:
             1. Set attenuation value to weak GNSS signal.
-            2. Kill XTRA/LTO daemon to support SUPL only case.
-            3. SUPL TTFF Cold Start for 10 iteration.
-
-        Expected Results:
-            All SUPL TTFF Cold Start results should be less than
-            weak_signal_supl_cs_criteria.
-        """
-        self.supl_ttff_weak_gnss_signal("cs", self.weak_signal_supl_cs_criteria)
-
-    @test_tracker_info(uuid="d72364d4-dad8-4d46-8190-87183def9822")
-    def test_supl_ttff_ws_weak_gnss_signal(self):
-        """Verify SUPL functionality of TTFF Warm Start under weak GNSS signal.
-
-        Steps:
-            1. Set attenuation value to weak GNSS signal.
-            2. Kill XTRA/LTO daemon to support SUPL only case.
-            3. SUPL TTFF Warm Start for 10 iteration.
-
-        Expected Results:
-            All SUPL TTFF Warm Start results should be less than
-            weak_signal_supl_ws_criteria.
-        """
-        self.supl_ttff_weak_gnss_signal("ws", self.weak_signal_supl_ws_criteria)
-
-    @test_tracker_info(uuid="aeb95733-9829-470d-bfc7-e3b059bf881f")
-    def test_supl_ttff_hs_weak_gnss_signal(self):
-        """Verify SUPL functionality of TTFF Hot Start under weak GNSS signal.
-
-        Steps:
-            1. Set attenuation value to weak GNSS signal.
-            2. Kill XTRA/LTO daemon to support SUPL only case.
-            3. SUPL TTFF Hot Start for 10 iteration.
-
-        Expected Results:
-            All SUPL TTFF Hot Start results should be less than
-            weak_signal_supl_hs_criteria.
-        """
-        self.supl_ttff_weak_gnss_signal("hs", self.weak_signal_supl_hs_criteria)
-
-    @test_tracker_info(uuid="4ad4a371-949a-42e1-b1f4-628c79fa8ddc")
-    def test_supl_factory_reset(self):
-        """Verify SUPL functionality after factory reset.
-
-        Steps:
-            1. Factory reset device.
-            2. Kill XTRA/LTO daemon to support SUPL only case.
-            3. SUPL TTFF Cold Start for 10 iteration.
-            4. Repeat Step 1. to Step 3. for 3 times.
-
-        Expected Results:
-            All SUPL TTFF Cold Start results should be within supl_cs_criteria.
-        """
-        for times in range(1, 4):
-            fastboot_factory_reset(self.ad, True)
-            self.ad.unlock_screen(password=None)
-            _init_device(self.ad)
-            begin_time = get_current_epoch_time()
-            kill_xtra_daemon(self.ad)
-            self.start_qxdm_and_tcpdump_log()
-            process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
-            start_ttff_by_gtw_gpstool(
-                self.ad, ttff_mode="cs", iteration=self.ttff_test_cycle)
-            ttff_data = process_ttff_by_gtw_gpstool(self.ad, begin_time,
-                                                    self.pixel_lab_location)
-            if not check_ttff_data(self.ad, ttff_data, ttff_mode="Cold Start",
-                                   criteria=self.supl_cs_criteria):
-                raise signals.TestFailure("SUPL after Factory Reset test %d "
-                                          "times -> FAIL" % times)
-            self.ad.log.info("SUPL after Factory Reset test %d times -> "
-                             "PASS" % times)
-
-    @test_tracker_info(uuid="ea3096cf-4f72-4e91-bfb3-0bcbfe865ab4")
-    def test_xtra_ttff_cs_mobile_data(self):
-        """Verify XTRA/LTO functionality of TTFF Cold Start with mobile data.
-
-        Steps:
-            1. Disable SUPL mode.
             2. TTFF Cold Start for 10 iteration.
 
         Expected Results:
-            XTRA/LTO TTFF Cold Start results should be within xtra_cs_criteria.
+            TTFF CS results should be within weak_signal_cs_criteria.
         """
-        self.xtra_ttff_mobile_data("cs", self.xtra_cs_criteria)
+        gutils.set_attenuator_gnss_signal(self.ad, self.attenuators,
+                                          self.weak_gnss_signal_attenuation)
+        gutils.run_ttff(self.ad, mode="cs", criteria=self.weak_signal_cs_criteria,
+                        test_cycle=self.ttff_test_cycle, base_lat_long=self.pixel_lab_location,
+                        collect_logs=self.collect_logs)
 
-    @test_tracker_info(uuid="c9b22894-deb3-4dc2-af14-4dcbb8ebad66")
-    def test_xtra_ttff_ws_mobile_data(self):
-        """Verify XTRA/LTO functionality of TTFF Warm Start with mobile data.
+    def test_ws_ttff_in_weak_gnss_signal(self):
+        """Verify TTFF warm start under weak GNSS signal.
 
         Steps:
-            1. Disable SUPL mode.
-            2. TTFF Warm Start for 10 iteration.
-
-        Expected Results:
-            XTRA/LTO TTFF Warm Start results should be within xtra_ws_criteria.
-        """
-        self.xtra_ttff_mobile_data("ws", self.xtra_ws_criteria)
-
-    @test_tracker_info(uuid="273741e2-0815-4817-96df-9c13401119dd")
-    def test_xtra_ttff_hs_mobile_data(self):
-        """Verify XTRA/LTO functionality of TTFF Hot Start with mobile data.
-
-        Steps:
-            1. Disable SUPL mode.
-            2. TTFF Hot Start for 10 iteration.
-
-        Expected Results:
-            XTRA/LTO TTFF Hot Start results should be within xtra_hs_criteria.
-        """
-        self.xtra_ttff_mobile_data("hs", self.xtra_hs_criteria)
-
-    @test_tracker_info(uuid="c91ba740-220e-41de-81e5-43af31f63907")
-    def test_xtra_ttff_cs_weak_gnss_signal(self):
-        """Verify XTRA/LTO functionality of TTFF Cold Start under weak GNSS
-        signal.
-
-        Steps:
-            1. Disable SUPL mode.
-            2. Set attenuation value to weak GNSS signal.
-            3. TTFF Cold Start for 10 iteration.
-
-        Expected Results:
-            XTRA/LTO TTFF Cold Start results should be within
-            weak_signal_xtra_cs_criteria.
-        """
-        self.xtra_ttff_weak_gnss_signal("cs", self.weak_signal_xtra_cs_criteria)
-
-    @test_tracker_info(uuid="2a285be7-3571-49fb-8825-01efa2e65f10")
-    def test_xtra_ttff_ws_weak_gnss_signal(self):
-        """Verify XTRA/LTO functionality of TTFF Warm Start under weak GNSS
-        signal.
-
-        Steps:
-            1. Disable SUPL mode.
             2. Set attenuation value to weak GNSS signal.
             3. TTFF Warm Start for 10 iteration.
 
         Expected Results:
-            XTRA/LTO TTFF Warm Start results should be within
-            weak_signal_xtra_ws_criteria.
+            TTFF WS result should be within weak_signal_criteria.
         """
-        self.xtra_ttff_weak_gnss_signal("ws", self.weak_signal_xtra_ws_criteria)
+        gutils.set_attenuator_gnss_signal(self.ad, self.attenuators,
+                                          self.weak_gnss_signal_attenuation)
+        gutils.run_ttff(self.ad, mode="ws", criteria=self.weak_signal_criteria,
+                        test_cycle=self.ttff_test_cycle, base_lat_long=self.pixel_lab_location,
+                        collect_logs=self.collect_logs)
 
-    @test_tracker_info(uuid="249bf484-8b04-4cd9-a372-aa718e5f4ec6")
-    def test_xtra_ttff_hs_weak_gnss_signal(self):
-        """Verify XTRA/LTO functionality of TTFF Hot Start under weak GNSS
-        signal.
+    def test_hs_ttff_in_weak_gnss_signal(self):
+        """Verify TTFF hot start under weak GNSS signal.
 
         Steps:
-            1. Disable SUPL mode.
             2. Set attenuation value to weak GNSS signal.
             3. TTFF Hot Start for 10 iteration.
 
         Expected Results:
-            XTRA/LTO TTFF Hot Start results should be within
-            weak_signal_xtra_hs_criteria.
+            TTFF HS result should be within weak_signal_criteria.
         """
-        self.xtra_ttff_weak_gnss_signal("hs", self.weak_signal_xtra_hs_criteria)
+        gutils.set_attenuator_gnss_signal(self.ad, self.attenuators,
+                                          self.weak_gnss_signal_attenuation)
+        gutils.run_ttff(self.ad, mode="hs", criteria=self.weak_signal_criteria,
+                        test_cycle=self.ttff_test_cycle, base_lat_long=self.pixel_lab_location,
+                        collect_logs=self.collect_logs)
 
-    @test_tracker_info(uuid="beeb3454-bcb2-451e-83fb-26289e89b515")
-    def test_xtra_ttff_cs_wifi(self):
-        """Verify XTRA/LTO functionality of TTFF Cold Start with WiFi.
-
-        Steps:
-            1. Disable SUPL mode and turn airplane mode on.
-            2. Connect to WiFi.
-            3. TTFF Cold Start for 10 iteration.
-
-        Expected Results:
-            XTRA/LTO TTFF Cold Start results should be within
-            xtra_cs_criteria.
-        """
-        self.xtra_ttff_wifi("cs", self.xtra_cs_criteria)
-
-    @test_tracker_info(uuid="f6e79b31-99d5-49ca-974f-4543957ea449")
-    def test_xtra_ttff_ws_wifi(self):
-        """Verify XTRA/LTO functionality of TTFF Warm Start with WiFi.
-
-        Steps:
-            1. Disable SUPL mode and turn airplane mode on.
-            2. Connect to WiFi.
-            3. TTFF Warm Start for 10 iteration.
-
-        Expected Results:
-            XTRA/LTO TTFF Warm Start results should be within xtra_ws_criteria.
-        """
-        self.xtra_ttff_wifi("ws", self.xtra_ws_criteria)
-
-    @test_tracker_info(uuid="8981363c-f64f-4c37-9674-46733c40473b")
-    def test_xtra_ttff_hs_wifi(self):
-        """Verify XTRA/LTO functionality of TTFF Hot Start with WiFi.
-
-        Steps:
-            1. Disable SUPL mode and turn airplane mode on.
-            2. Connect to WiFi.
-            3. TTFF Hot Start for 10 iteration.
-
-        Expected Results:
-            XTRA/LTO TTFF Hot Start results should be within xtra_hs_criteria.
-        """
-        self.xtra_ttff_wifi("hs", self.xtra_hs_criteria)
-
-    @test_tracker_info(uuid="1745b8a4-5925-4aa0-809a-1b17e848dc9c")
-    def test_xtra_modem_ssr(self):
-        """Verify XTRA/LTO functionality after modem silent reboot /
-        GPS daemons restart.
-
-        Steps:
-            1. Trigger modem crash by adb/Restart GPS daemons by killing PID.
-            2. Wait 1 minute for modem to recover.
-            3. XTRA/LTO TTFF Cold Start for 3 iteration.
-            4. Repeat Step1. to Step 3. for 5 times.
-
-        Expected Results:
-            All XTRA/LTO TTFF Cold Start results should be within
-            xtra_cs_criteria.
-        """
-        xtra_ssr_test_result_all = []
-        disable_supl_mode(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        for times in range(1, 6):
-            begin_time = get_current_epoch_time()
-            if gutils.check_chipset_vendor_by_qualcomm(self.ad):
-                test_info = "XTRA after Modem SSR"
-                gnss_trigger_modem_ssr_by_mds(self.ad)
-            else:
-                test_info = "LTO after restarting GPS daemons"
-                gutils.restart_gps_daemons(self.ad)
-            if not verify_internet_connection(self.ad.log, self.ad, retries=3,
-                                              expected_state=True):
-                raise signals.TestFailure("Fail to connect to LTE network.")
-            process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
-            start_ttff_by_gtw_gpstool(self.ad, ttff_mode="cs", iteration=3)
-            ttff_data = process_ttff_by_gtw_gpstool(self.ad, begin_time,
-                                                    self.pixel_lab_location)
-            xtra_ssr_test_result = check_ttff_data(
-                self.ad, ttff_data, ttff_mode="Cold Start",
-                criteria=self.xtra_cs_criteria)
-            self.ad.log.info("%s test %d times -> %s" % (
-                test_info, times, xtra_ssr_test_result))
-            xtra_ssr_test_result_all.append(xtra_ssr_test_result)
-        asserts.assert_true(all(xtra_ssr_test_result_all),
-                            "TTFF fails to reach designated criteria")
-
-    @test_tracker_info(uuid="4d6e81e1-3abb-4e03-b732-7b6b497a2258")
-    def test_xtra_download_mobile_data(self):
-        """Verify XTRA/LTO data could be downloaded via mobile data.
-
-        Steps:
-            1. Delete all GNSS aiding data.
-            2. Get location fixed.
-            3. Verify whether XTRA/LTO is downloaded and injected.
-            4. Repeat Step 1. to Step 3. for 5 times.
-
-        Expected Results:
-            XTRA/LTO data is properly downloaded and injected via mobile data.
-        """
-        mobile_xtra_result_all = []
-        disable_supl_mode(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        for i in range(1, 6):
-            begin_time = get_current_epoch_time()
-            process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
-            time.sleep(5)
-            start_gnss_by_gtw_gpstool(self.ad, False)
-            mobile_xtra_result = check_xtra_download(self.ad, begin_time)
-            self.ad.log.info("Iteration %d => %s" % (i, mobile_xtra_result))
-            mobile_xtra_result_all.append(mobile_xtra_result)
-        asserts.assert_true(all(mobile_xtra_result_all),
-                            "Fail to Download and Inject XTRA/LTO File.")
-
-    @test_tracker_info(uuid="625ac665-1446-4406-a722-e6a19645222c")
-    def test_xtra_download_wifi(self):
-        """Verify XTRA/LTO data could be downloaded via WiFi.
-
-        Steps:
-            1. Connect to WiFi.
-            2. Delete all GNSS aiding data.
-            3. Get location fixed.
-            4. Verify whether XTRA/LTO is downloaded and injected.
-            5. Repeat Step 2. to Step 4. for 5 times.
-
-        Expected Results:
-            XTRA data is properly downloaded and injected via WiFi.
-        """
-        wifi_xtra_result_all = []
-        disable_supl_mode(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        self.ad.log.info("Turn airplane mode on")
-        self.ad.droid.connectivityToggleAirplaneMode(True)
-        wifi_toggle_state(self.ad, True)
-        connect_to_wifi_network(
-            self.ad, self.ssid_map[self.pixel_lab_network[0]["SSID"]])
-        for i in range(1, 6):
-            begin_time = get_current_epoch_time()
-            process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
-            time.sleep(5)
-            start_gnss_by_gtw_gpstool(self.ad, False)
-            wifi_xtra_result = check_xtra_download(self.ad, begin_time)
-            wifi_xtra_result_all.append(wifi_xtra_result)
-            self.ad.log.info("Iteration %d => %s" % (i, wifi_xtra_result))
-        asserts.assert_true(all(wifi_xtra_result_all),
-                            "Fail to Download and Inject XTRA/LTO File.")
-
-    @test_tracker_info(uuid="2a9f2890-3c0a-48b8-821d-bf97e36355e9")
     def test_quick_toggle_gnss_state(self):
         """Verify GNSS can still work properly after quick toggle GNSS off
         to on.
@@ -1219,70 +590,10 @@
         Expected Results:
             No single Timeout is seen in 10 iterations.
         """
-        enable_supl_mode(self.ad)
-        self.start_qxdm_and_tcpdump_log()
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
         start_toggle_gnss_by_gtw_gpstool(
             self.ad, iteration=self.ttff_test_cycle)
 
-    @test_tracker_info(uuid="9f565b32-9938-42c0-a29d-f4d28b5f4d75")
-    def test_supl_system_server_restart(self):
-        """Verify SUPL functionality after system server restart.
-
-        Steps:
-            1. Kill XTRA/LTO daemon to support SUPL only case.
-            2. Get location fixed within supl_cs_criteria.
-            3. Restarts android runtime.
-            4. Get location fixed within supl_cs_criteria.
-
-        Expected Results:
-            Location fixed within supl_cs_criteria.
-        """
-        overall_test_result = []
-        kill_xtra_daemon(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        for test_loop in range(1, 6):
-            process_gnss_by_gtw_gpstool(self.ad, self.supl_cs_criteria)
-            start_gnss_by_gtw_gpstool(self.ad, False)
-            self.ad.restart_runtime()
-            self.ad.unlock_screen(password=None)
-            test_result = process_gnss_by_gtw_gpstool(self.ad,
-                                                      self.supl_cs_criteria)
-            start_gnss_by_gtw_gpstool(self.ad, False)
-            self.ad.log.info("Iteration %d => %s" % (test_loop, test_result))
-            overall_test_result.append(test_result)
-        asserts.assert_true(all(overall_test_result),
-                            "SUPL fail after system server restart.")
-
-    @test_tracker_info(uuid="a9a64900-9016-46d0-ad7e-cab30e8152cd")
-    def test_xtra_system_server_restart(self):
-        """Verify XTRA/LTO functionality after system server restart.
-
-        Steps:
-            1. Disable SUPL mode.
-            2. Get location fixed within xtra_cs_criteria.
-            3. Restarts android runtime.
-            4. Get location fixed within xtra_cs_criteria.
-
-        Expected Results:
-            Location fixed within xtra_cs_criteria.
-        """
-        overall_test_result = []
-        disable_supl_mode(self.ad)
-        self.start_qxdm_and_tcpdump_log()
-        for test_loop in range(1, 6):
-            process_gnss_by_gtw_gpstool(self.ad, self.xtra_cs_criteria)
-            start_gnss_by_gtw_gpstool(self.ad, False)
-            self.ad.restart_runtime()
-            self.ad.unlock_screen(password=None)
-            test_result = process_gnss_by_gtw_gpstool(self.ad,
-                                                      self.xtra_cs_criteria)
-            start_gnss_by_gtw_gpstool(self.ad, False)
-            self.ad.log.info("Iteration %d => %s" % (test_loop, test_result))
-            overall_test_result.append(test_result)
-        asserts.assert_true(all(overall_test_result),
-                            "XTRA/LTO fail after system server restart.")
-
-    @test_tracker_info(uuid="ab5ef9f7-0b28-48ed-a693-7f1d902ca3e1")
     def test_gnss_init_after_reboot(self):
         """Verify SUPL and XTRA/LTO functionality after reboot.
 
@@ -1296,12 +607,14 @@
             Location fixed within supl_hs_criteria.
         """
         overall_test_result = []
-        enable_supl_mode(self.ad)
+        # As b/252971345 requests, we need the log before reboot for debugging.
+        gutils.start_pixel_logger(self.ad)
         process_gnss_by_gtw_gpstool(self.ad, self.supl_cs_criteria)
         start_gnss_by_gtw_gpstool(self.ad, False)
+        gutils.stop_pixel_logger(self.ad)
         for test_loop in range(1, 11):
             reboot(self.ad)
-            self.start_qxdm_and_tcpdump_log()
+            gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
             if is_device_wearable(self.ad):
                 test_result = process_gnss_by_gtw_gpstool(
                     self.ad, self.wearable_reboot_hs_criteria, clear_data=False)
@@ -1318,63 +631,184 @@
         asserts.assert_true(all(overall_test_result),
                             "GNSS init fail after reboot.")
 
-    @test_tracker_info(uuid="2c62183a-4354-4efc-92f2-84580cbd3398")
-    def test_lto_download_after_reboot(self):
-        """Verify LTO data could be downloaded and injected after device reboot.
+    def test_host_gnssstatus_validation(self):
+        """Verify GnssStatus integrity during host tracking for 1 minute.
 
         Steps:
-            1. Reboot device.
-            2. Verify whether LTO is auto downloaded and injected without trigger GPS.
-            3. Repeat Step 1 to Step 2 for 5 times.
+            1. Launch GTW_GPSTool.
+            2. GNSS tracking for 1 minute with 1 second frequency.
+            3. Validate all the GnssStatus raw data.(SV, SVID, Elev, Azim)
 
         Expected Results:
-            LTO data is properly downloaded and injected at the first time tether to phone.
+            GnssStatus obj should return no failures
         """
-        reboot_lto_test_results_all = []
-        disable_supl_mode(self.ad)
-        for times in range(1, 6):
-            delete_lto_file(self.ad)
-            reboot(self.ad)
-            self.start_qxdm_and_tcpdump_log()
-            # Wait 20 seconds for boot busy and lto auto-download time
-            time.sleep(20)
-            begin_time = get_current_epoch_time()
-            reboot_lto_test_result = gutils.check_xtra_download(self.ad, begin_time)
-            self.ad.log.info("Iteration %d => %s" % (times, reboot_lto_test_result))
-            reboot_lto_test_results_all.append(reboot_lto_test_result)
-            gutils.stop_pixel_logger(self.ad)
-            tutils.stop_adb_tcpdump(self.ad)
-        asserts.assert_true(all(reboot_lto_test_results_all),
-                                "Fail to Download and Inject LTO File.")
+        gnss_tracking_via_gtw_gpstool(self.ad, self.standalone_cs_criteria,
+                                      api_type="gnss", testtime=1)
+        parse_gtw_gpstool_log(self.ad, self.pixel_lab_location, api_type="gnss",
+                              validate_gnssstatus=True)
 
-    @test_tracker_info(uuid="a7048a4f-8a40-40a4-bb6c-7fc90e8227bd")
-    def test_ws_with_assist(self):
-        """Verify Warm Start functionality with existed LTO data.
+    def test_onchip_gnssstatus_validation(self):
+        """Verify GnssStatus integrity during onchip tracking for 1 minute.
 
         Steps:
-            1. Disable SUPL mode.
-            2. Make LTO is downloaded.
-            3. Turn on AirPlane mode to make sure there's no network connection.
-            4. TTFF Warm Start with Assist for 10 iteration.
+            1. Launch GTW_GPSTool.
+            2. GNSS tracking for 1 minute with 6 second frequency.
+            3. Validate all the GnssStatus raw data.(SV, SVID, Elev, Azim)
 
         Expected Results:
-            All TTFF Warm Start with Assist results should be within
-            xtra_ws_criteria.
+            GnssStatus obj should return no failures
         """
-        self.ttff_with_assist("ws", self.xtra_ws_criteria)
+        if gutils.check_chipset_vendor_by_qualcomm(self.ad):
+            raise signals.TestSkip("Not BRCM chipset. Skip the test.")
+        gnss_tracking_via_gtw_gpstool(self.ad, self.standalone_cs_criteria,
+                                      api_type="gnss", testtime=1, freq=self.onchip_interval)
+        parse_gtw_gpstool_log(self.ad, self.pixel_lab_location, api_type="gnss",
+                              validate_gnssstatus=True)
 
-    @test_tracker_info(uuid="c5fb9519-63b0-42bd-bd79-fce7593604ea")
-    def test_cs_with_assist(self):
-        """Verify Cold Start functionality with existed LTO data.
-
-        Steps:
-            1. Disable SUPL mode.
-            2. Make sure LTO is downloaded.
-            3. Turn on AirPlane mode to make sure there's no network connection.
-            4. TTFF Cold Start with Assist for 10 iteration.
-
-        Expected Results:
-            All TTFF Cold Start with Assist results should be within
-            standalone_cs_criteria.
+    def test_location_update_after_resuming_from_deep_suspend(self):
+        """Verify the GPS location reported after resume from suspend mode
+        1. Enable GPS location report for 1 min to make sure the GPS is working
+        2. Force DUT into deep suspend mode for a while(3 times with 15s interval)
+        3. Enable GPS location report for 5 mins
+        4. Check the report frequency
+        5. Check the location fix rate
         """
-        self.ttff_with_assist("csa", self.standalone_cs_criteria)
+
+        gps_enable_minutes = 1
+        gnss_tracking_via_gtw_gpstool(self.ad, criteria=self.supl_cs_criteria, api_type="gnss",
+                                      testtime=gps_enable_minutes)
+        result = parse_gtw_gpstool_log(self.ad, self.pixel_lab_location, api_type="gnss")
+        self.ad.log.debug("Location report details before suspend")
+        self.ad.log.debug(result)
+        gutils.validate_location_fix_rate(self.ad, result, run_time=gps_enable_minutes,
+                                          fix_rate_criteria=0.95)
+
+        gutils.deep_suspend_device(self.ad)
+
+        gps_enable_minutes = 5
+        gnss_tracking_via_gtw_gpstool(self.ad, criteria=self.supl_cs_criteria, api_type="gnss",
+                                      testtime=gps_enable_minutes)
+        result = parse_gtw_gpstool_log(self.ad, self.pixel_lab_location, api_type="gnss")
+        self.ad.log.debug("Location report details after suspend")
+        self.ad.log.debug(result)
+
+        location_report_time = list(result.keys())
+        gutils.check_location_report_interval(self.ad, location_report_time,
+                                              gps_enable_minutes * 60, tolerance=0.01)
+        gutils.validate_location_fix_rate(self.ad, result, run_time=gps_enable_minutes,
+                                          fix_rate_criteria=0.99)
+
+    def test_location_mode_in_battery_saver_with_screen_off(self):
+        """Ensure location request with foreground permission can work
+        in battery saver mode (screen off)
+
+        1. unplug power
+        2. enter battery saver mode
+        3. start tracking for 2 mins with screen off
+        4. repest step 3 for 3 times
+        """
+        try:
+            gutils.set_battery_saver_mode(self.ad, state=True)
+            test_time = 2
+            for i in range(1, 4):
+                self.ad.log.info("Tracking attempt %s" % str(i))
+                gnss_tracking_via_gtw_gpstool(
+                    self.ad, criteria=self.supl_cs_criteria, api_type="gnss", testtime=test_time,
+                    is_screen_off=True)
+                result = parse_gtw_gpstool_log(self.ad, self.pixel_lab_location, api_type="gnss")
+                gutils.validate_location_fix_rate(self.ad, result, run_time=test_time,
+                                                  fix_rate_criteria=0.99)
+        finally:
+            gutils.set_battery_saver_mode(self.ad, state=False)
+
+    def test_measure_adr_rate_after_10_mins_tracking(self):
+        """Verify ADR rate
+
+        1. Enable "Force full gnss measurement"
+        2. Start tracking with GnssMeasurement enabled for 10 mins
+        3. Check ADR usable rate / valid rate
+        4. Disable "Force full gnss measurement"
+        """
+        adr_threshold = self.adr_ratio_threshold.get(self.ad.model)
+        if not adr_threshold:
+            self.ad.log.warn((f"Can't get '{self.ad.model}' threshold from config "
+                              f"{self.adr_ratio_threshold}, use default threshold 0.5"))
+            adr_threshold = 0.5
+        with gutils.full_gnss_measurement(self.ad):
+            gnss_tracking_via_gtw_gpstool(self.ad, criteria=self.supl_cs_criteria, api_type="gnss",
+                                          testtime=10, meas_flag=True)
+            gutils.validate_adr_rate(self.ad, pass_criteria=float(adr_threshold))
+
+
+    def test_hal_crashing_should_resume_tracking(self):
+        """Make sure location request can be resumed after HAL restart.
+
+        1. Start GPS tool and get First Fixed
+        2. Wait for 1 min for tracking
+        3. Restart HAL service
+        4. Wait for 1 min for tracking
+        5. Check fix rate
+        """
+
+        first_fixed_time = process_gnss_by_gtw_gpstool(self.ad, criteria=self.supl_cs_criteria)
+        begin_time = int(first_fixed_time.timestamp() * 1000)
+
+        self.ad.log.info("Start 2 mins tracking")
+
+        gutils.wait_n_mins_for_gnss_tracking(self.ad, begin_time, testtime=1,
+                                             ignore_hal_crash=False)
+        gutils.restart_hal_service(self.ad)
+        # The test case is designed to run the tracking for 2 mins, so we assign testime to 2 to
+        # indicate the total run time is 2 mins (starting from begin_time).
+        gutils.wait_n_mins_for_gnss_tracking(self.ad, begin_time, testtime=2, ignore_hal_crash=True)
+
+        start_gnss_by_gtw_gpstool(self.ad, state=False)
+
+        result = parse_gtw_gpstool_log(self.ad, self.pixel_lab_location)
+        gutils.validate_location_fix_rate(self.ad, result, run_time=2,
+                                          fix_rate_criteria=0.95)
+
+
+    def test_power_save_mode_should_apply_latest_measurement_setting(self):
+        """Ensure power save mode will apply the GNSS measurement setting.
+
+        1. Turn off full GNSS measurement.
+        2. Run tracking for 2 mins
+        3. Check the power save mode status
+        4. Turn on full GNSS measurement and re-register measurement callback
+        6. Run tracking for 30s
+        7. Check the power save mode status
+        8. Turn off full GNSS measurement and re-register measurement callback
+        9. Run tracking for 2 mins
+        10. Check the power save mode status
+        """
+        def wait_for_power_state_changes(wait_time):
+            gutils.re_register_measurement_callback(self.ad)
+            tracking_begin_time = get_current_epoch_time()
+            gutils.wait_n_mins_for_gnss_tracking(self.ad, tracking_begin_time, testtime=wait_time)
+            return tracking_begin_time
+
+        if self.ad.model.lower() == "sunfish":
+            raise signals.TestSkip(
+                "According to b/241049795, it's HW issue and won't be fixed.")
+
+        gutils.start_pixel_logger(self.ad)
+        with gutils.run_gnss_tracking(self.ad, criteria=self.supl_cs_criteria, meas_flag=True):
+            start_time = wait_for_power_state_changes(wait_time=2)
+            gutils.check_power_save_mode_status(
+                self.ad, full_power=False, begin_time=start_time,
+                brcm_error_allowlist=self.brcm_error_log_allowlist)
+
+            with gutils.full_gnss_measurement(self.ad):
+                start_time = wait_for_power_state_changes(wait_time=0.5)
+                gutils.check_power_save_mode_status(
+                    self.ad, full_power=True, begin_time=start_time,
+                    brcm_error_allowlist=self.brcm_error_log_allowlist)
+
+            start_time = wait_for_power_state_changes(wait_time=2)
+            gutils.check_power_save_mode_status(
+                self.ad, full_power=False, begin_time=start_time,
+                brcm_error_allowlist=self.brcm_error_log_allowlist)
+
+        gutils.stop_pixel_logger(self.ad)
+
diff --git a/acts_tests/tests/google/gnss/GnssHsSenTest.py b/acts_tests/tests/google/gnss/GnssHsSenTest.py
index 5269ae0..bd4e398 100644
--- a/acts_tests/tests/google/gnss/GnssHsSenTest.py
+++ b/acts_tests/tests/google/gnss/GnssHsSenTest.py
@@ -13,11 +13,12 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
+"""Lab GNSS Hot Start Sensitivity Test"""
 
 import os
 from acts_contrib.test_utils.gnss.GnssBlankingBase import GnssBlankingBase
 from acts_contrib.test_utils.gnss.dut_log_test_utils import get_gpstool_logs
-from acts_contrib.test_utils.gnss.gnss_test_utils import excute_eecoexer_function
+from acts_contrib.test_utils.gnss.gnss_test_utils import execute_eecoexer_function
 
 
 class GnssHsSenTest(GnssBlankingBase):
@@ -25,11 +26,19 @@
 
     def __init__(self, controllers):
         super().__init__(controllers)
-        self.gnss_simulator_power_level = -130
-        self.sa_sensitivity = -150
-        self.gnss_pwr_lvl_offset = 5
+        self.cell_tx_ant = None
+        self.cell_pwr = None
+        self.eecoex_func = ''
+        self.coex_stop_cmd = ''
+        self.coex_params = {}
 
-    def gnss_hot_start_sensitivity_search_base(self, cellular_enable=False):
+    def setup_class(self):
+        super().setup_class()
+        self.coex_params = self.user_params.get('coex_params', {})
+        self.cell_tx_ant = self.coex_params.get('cell_tx_ant', 'PRIMARY')
+        self.cell_pwr = self.coex_params.get('cell_pwr', 'Infinity')
+
+    def gnss_hot_start_sensitivity_search_base(self, coex_enable=False):
         """
         Perform GNSS hot start sensitivity search.
 
@@ -41,49 +50,32 @@
         # Get parameters from user_params.
         first_wait = self.user_params.get('first_wait', 300)
         wait_between_pwr = self.user_params.get('wait_between_pwr', 60)
-        gnss_pwr_sweep = self.user_params.get('gnss_pwr_sweep')
-        gnss_init_pwr = gnss_pwr_sweep.get('init')
-        self.gnss_simulator_power_level = gnss_init_pwr[0]
-        self.sa_sensitivity = gnss_init_pwr[1]
-        self.gnss_pwr_lvl_offset = gnss_init_pwr[2]
-        gnss_pwr_fine_sweep = gnss_pwr_sweep.get('fine_sweep')
         ttft_iteration = self.user_params.get('ttff_iteration', 25)
 
         # Start the test item with gnss_init_power_setting.
-        if self.gnss_init_power_setting(first_wait):
-            self.log.info('Successfully set the GNSS power level to %d' %
-                          self.sa_sensitivity)
+        ret, pwr_lvl = self.gnss_init_power_setting(first_wait)
+        if ret:
+            self.log.info(f'Successfully set the GNSS power level to {pwr_lvl}')
             # Create gnss log folders for init and cellular sweep
             gnss_init_log_dir = os.path.join(self.gnss_log_path, 'GNSS_init')
 
             # Pull all exist GPStool logs into GNSS_init folder
             get_gpstool_logs(self.dut, gnss_init_log_dir, False)
-
-            if cellular_enable:
-                self.log.info('Start cellular coexistence test.')
-                # Set cellular Tx power level.
-                eecoex_cmd = self.eecoex_func.format('Infinity')
-                eecoex_cmd_file_str = eecoex_cmd.replace(',', '_')
-                excute_eecoexer_function(self.dut, eecoex_cmd)
-            else:
-                self.log.info('Start stand alone test.')
-                eecoex_cmd_file_str = 'Stand_alone'
-
-            for i, gnss_pwr in enumerate(gnss_pwr_fine_sweep):
-                self.log.info('Start fine GNSS power level sweep part %d' %
-                              (i + 1))
-                sweep_start = gnss_pwr[0]
-                sweep_stop = gnss_pwr[1]
-                sweep_offset = gnss_pwr[2]
-                self.log.info(
-                    'The GNSS simulator (start, stop, offset): (%.1f, %.1f, %.1f)'
-                    % (sweep_start, sweep_stop, sweep_offset))
-                result, sensitivity = self.hot_start_gnss_power_sweep(
-                    sweep_start, sweep_stop, sweep_offset, wait_between_pwr,
-                    ttft_iteration, True, eecoex_cmd_file_str)
-                if not result:
-                    break
-            self.log.info('The sensitivity level is: %.1f' % sensitivity)
+        if coex_enable:
+            self.log.info('Start coexistence test.')
+            eecoex_cmd_file_str = self.eecoex_func.replace(',', '_')
+            execute_eecoexer_function(self.dut, self.eecoex_func)
+        else:
+            self.log.info('Start stand alone test.')
+            eecoex_cmd_file_str = 'Stand_alone'
+        for i, gnss_pwr_swp in enumerate(self.gnss_pwr_sweep_fine_sweep_ls):
+            self.log.info(f'Start fine GNSS power level sweep part {i + 1}')
+            result, sensitivity = self.hot_start_gnss_power_sweep(
+                gnss_pwr_swp, wait_between_pwr, ttft_iteration, True,
+                eecoex_cmd_file_str)
+            if not result:
+                break
+        self.log.info(f'The sensitivity level is: {sensitivity}')
 
     def test_hot_start_sensitivity_search(self):
         """
@@ -95,86 +87,127 @@
         """
         GNSS hot start GSM850 Ch190 coexistence sensitivity search.
         """
-        self.eecoex_func = 'CELLR,2,850,190,1,1,{}'
-        self.log.info('Running GSM850 and GNSS coexistence sensitivity search.')
+        self.eecoex_func = f'CELLR,2,850,190,1,{self.cell_tx_ant},{self.cell_pwr}'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = f'Running GSM850 with {self.cell_tx_ant} antenna \
+                and GNSS coexistence sensitivity search.'
+
+        self.log.info(msg)
         self.gnss_hot_start_sensitivity_search_base(True)
 
     def test_hot_start_sensitivity_search_gsm900(self):
         """
         GNSS hot start GSM900 Ch20 coexistence sensitivity search.
         """
-        self.eecoex_func = 'CELLR,2,900,20,1,1,{}'
-        self.log.info('Running GSM900 and GNSS coexistence sensitivity search.')
+        self.eecoex_func = f'CELLR,2,900,20,1,{self.cell_tx_ant},{self.cell_pwr}'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = f'Running GSM900 with {self.cell_tx_ant} \
+                antenna and GNSS coexistence sensitivity search.'
+
+        self.log.info(msg)
         self.gnss_hot_start_sensitivity_search_base(True)
 
     def test_hot_start_sensitivity_search_gsm1800(self):
         """
         GNSS hot start GSM1800 Ch699 coexistence sensitivity search.
         """
-        self.eecoex_func = 'CELLR,2,1800,699,1,1,{}'
-        self.log.info(
-            'Running GSM1800 and GNSS coexistence sensitivity search.')
+        self.eecoex_func = f'CELLR,2,1800,699,1,{self.cell_tx_ant},{self.cell_pwr}'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = f'Running GSM1800 {self.cell_tx_ant} antenna and GNSS coexistence sensitivity search.'
+        self.log.info(msg)
         self.gnss_hot_start_sensitivity_search_base(True)
 
     def test_hot_start_sensitivity_search_gsm1900(self):
         """
         GNSS hot start GSM1900 Ch661 coexistence sensitivity search.
         """
-        self.eecoex_func = 'CELLR,2,1900,661,1,1,{}'
-        self.log.info(
-            'Running GSM1900 and GNSS coexistence sensitivity search.')
+        self.eecoex_func = f'CELLR,2,1900,661,1,{self.cell_tx_ant},{self.cell_pwr}'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = f'Running GSM1900 {self.cell_tx_ant} antenna and GNSS coexistence sensitivity search.'
+        self.log.info(msg)
         self.gnss_hot_start_sensitivity_search_base(True)
 
     def test_hot_start_sensitivity_search_lte_b38(self):
         """
         GNSS hot start LTE B38 Ch38000 coexistence sensitivity search.
         """
-        self.eecoex_func = 'CELLR,5,38,38000,true,PRIMARY,{},10MHz,0,12'
-        self.log.info(
-            'Running LTE B38 and GNSS coexistence sensitivity search.')
+        self.eecoex_func = f'CELLR,5,38,38000,true,{self.cell_tx_ant},{self.cell_pwr},10MHz,0,12'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = f'Running LTE B38 {self.cell_tx_ant} antenna and GNSS coexistence sensitivity search.'
+        self.log.info(msg)
         self.gnss_hot_start_sensitivity_search_base(True)
 
     def test_hot_start_sensitivity_search_lte_b39(self):
         """
         GNSS hot start LTE B39 Ch38450 coexistence sensitivity search.
         """
-        self.eecoex_func = 'CELLR,5,39,38450,true,PRIMARY,{},10MHz,0,12'
-        self.log.info(
-            'Running LTE B38 and GNSS coexistence sensitivity search.')
+        self.eecoex_func = f'CELLR,5,39,38450,true,{self.cell_tx_ant},{self.cell_pwr},10MHz,0,12'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = f'Running LTE B38 {self.cell_tx_ant} antenna and GNSS coexistence sensitivity search.'
+        self.log.info(msg)
         self.gnss_hot_start_sensitivity_search_base(True)
 
     def test_hot_start_sensitivity_search_lte_b40(self):
         """
         GNSS hot start LTE B40 Ch39150 coexistence sensitivity search.
         """
-        self.eecoex_func = 'CELLR,5,40,39150,true,PRIMARY,{},10MHz,0,12'
-        self.log.info(
-            'Running LTE B38 and GNSS coexistence sensitivity search.')
+        self.eecoex_func = f'CELLR,5,40,39150,true,{self.cell_tx_ant},{self.cell_pwr},10MHz,0,12'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = f'Running LTE B38 {self.cell_tx_ant} antenna and GNSS coexistence sensitivity search.'
+        self.log.info(msg)
         self.gnss_hot_start_sensitivity_search_base(True)
 
     def test_hot_start_sensitivity_search_lte_b41(self):
         """
         GNSS hot start LTE B41 Ch40620 coexistence sensitivity search.
         """
-        self.eecoex_func = 'CELLR,5,41,40620,true,PRIMARY,{},10MHz,0,12'
-        self.log.info(
-            'Running LTE B41 and GNSS coexistence sensitivity search.')
+        self.eecoex_func = f'CELLR,5,41,40620,true,{self.cell_tx_ant},{self.cell_pwr},10MHz,0,12'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = f'Running LTE B41 {self.cell_tx_ant} antenna and GNSS coexistence sensitivity search.'
+        self.log.info(msg)
         self.gnss_hot_start_sensitivity_search_base(True)
 
     def test_hot_start_sensitivity_search_lte_b42(self):
         """
         GNSS hot start LTE B42 Ch42590 coexistence sensitivity search.
         """
-        self.eecoex_func = 'CELLR,5,42,42590,true,PRIMARY,{},10MHz,0,12'
-        self.log.info(
-            'Running LTE B42 and GNSS coexistence sensitivity search.')
+        self.eecoex_func = f'CELLR,5,42,42590,true,{self.cell_tx_ant},{self.cell_pwr},10MHz,0,12'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = f'Running LTE B42 {self.cell_tx_ant} antenna and GNSS coexistence sensitivity search.'
+        self.log.info(msg)
         self.gnss_hot_start_sensitivity_search_base(True)
 
     def test_hot_start_sensitivity_search_lte_b48(self):
         """
         GNSS hot start LTE B48 Ch55990 coexistence sensitivity search.
         """
-        self.eecoex_func = 'CELLR,5,48,55990,true,PRIMARY,{},10MHz,0,12'
-        self.log.info(
-            'Running LTE B48 and GNSS coexistence sensitivity search.')
+        self.eecoex_func = f'CELLR,5,48,55990,true,{self.cell_tx_ant},{self.cell_pwr},10MHz,0,12'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = f'Running LTE B48 {self.cell_tx_ant} antenna and GNSS coexistence sensitivity search.'
+        self.log.info(msg)
         self.gnss_hot_start_sensitivity_search_base(True)
+
+    def test_hot_start_sensitivity_search_fr2_n2605(self):
+        """
+        GNSS hot start 5G NR B260 CH2234165 coexistence sensitivity search.
+        """
+        self.eecoex_func = f'CELLR,30,260,2234165,183,{self.cell_pwr}'
+        self.coex_stop_cmd = 'CELLR,19'
+        msg = 'Running 5G NR B260 CH2234165 and GNSS coexistence sensitivity search.'
+        self.log.info(msg)
+        self.gnss_hot_start_sensitivity_search_base(True)
+
+    def test_hot_start_sensitivity_custom_case(self):
+        """
+        GNSS hot start custom case coexistence sensitivity search.
+        """
+        cust_cmd = self.coex_params.get('custom_cmd', '')
+        cust_stop_cmd = self.coex_params.get('custom_stop_cmd', '')
+        if cust_cmd and cust_stop_cmd:
+            self.eecoex_func = cust_cmd
+            self.coex_stop_cmd = cust_stop_cmd
+            msg = f'Running custom {self.eecoex_func} and GNSS coexistence sensitivity search.'
+            self.log.info(msg)
+            self.gnss_hot_start_sensitivity_search_base(True)
+        else:
+            self.log.warning('No custom coex command is provided')
diff --git a/acts_tests/tests/google/gnss/GnssSimInventoryTest.py b/acts_tests/tests/google/gnss/GnssSimInventoryTest.py
index 801aa85..4e7ce69 100644
--- a/acts_tests/tests/google/gnss/GnssSimInventoryTest.py
+++ b/acts_tests/tests/google/gnss/GnssSimInventoryTest.py
@@ -1,4 +1,3 @@
-import time
 import os
 import re
 
@@ -12,6 +11,7 @@
 
 class GnssSimInventoryTest(BaseTestClass):
     """ GNSS SIM Inventory Tests"""
+
     def setup_class(self):
         super().setup_class()
         self.ad = self.android_devices[0]
diff --git a/acts_tests/tests/google/gnss/GnssSuplTest.py b/acts_tests/tests/google/gnss/GnssSuplTest.py
new file mode 100644
index 0000000..853a228
--- /dev/null
+++ b/acts_tests/tests/google/gnss/GnssSuplTest.py
@@ -0,0 +1,294 @@
+from multiprocessing import Process
+import time
+
+from acts import asserts
+from acts import signals
+from acts.base_test import BaseTestClass
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
+from acts_contrib.test_utils.gnss import supl
+from acts_contrib.test_utils.gnss import gnss_defines
+from acts_contrib.test_utils.gnss.testtracker_util import log_testtracker_uuid
+from acts_contrib.test_utils.tel.tel_data_utils import http_file_download_by_sl4a
+from acts_contrib.test_utils.tel.tel_logging_utils import get_tcpdump_log
+from acts_contrib.test_utils.tel.tel_logging_utils import stop_adb_tcpdump
+from acts_contrib.test_utils.tel.tel_logging_utils import get_tcpdump_log
+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 verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts.utils import get_current_epoch_time
+
+
+class GnssSuplTest(BaseTestClass):
+    def setup_class(self):
+        super().setup_class()
+        self.ad = self.android_devices[0]
+        req_params = [
+            "pixel_lab_network", "standalone_cs_criteria", "supl_cs_criteria", "supl_ws_criteria",
+            "supl_hs_criteria", "default_gnss_signal_attenuation", "pixel_lab_location",
+            "qdsp6m_path", "collect_logs", "ttff_test_cycle",
+            "supl_capabilities", "no_gnss_signal_attenuation", "set_attenuator"
+        ]
+        self.unpack_userparams(req_param_names=req_params)
+        # create hashmap for SSID
+        self.ssid_map = {}
+        for network in self.pixel_lab_network:
+            SSID = network["SSID"]
+            self.ssid_map[SSID] = network
+        self.init_device()
+
+    def only_brcm_device_runs_wifi_case(self):
+        """SUPL over wifi is only supported by BRCM devices, for QUAL device, skip the test.
+        """
+        if gutils.check_chipset_vendor_by_qualcomm(self.ad):
+            raise signals.TestSkip("Qualcomm device doesn't support SUPL over wifi")
+
+    def wearable_btwifi_should_skip_mobile_data_case(self):
+        if gutils.is_wearable_btwifi(self.ad):
+            raise signals.TestSkip("Skip mobile data case for BtWiFi sku")
+
+    def init_device(self):
+        """Init GNSS test devices for SUPL suite."""
+        gutils._init_device(self.ad)
+        gutils.disable_vendor_orbit_assistance_data(self.ad)
+        gutils.enable_supl_mode(self.ad)
+        self.enable_supl_over_wifi()
+        gutils.reboot(self.ad)
+
+    def enable_supl_over_wifi(self):
+        if not gutils.check_chipset_vendor_by_qualcomm(self.ad):
+            supl.set_supl_over_wifi_state(self.ad, turn_on=True)
+
+    def setup_test(self):
+        gutils.log_current_epoch_time(self.ad, "test_start_time")
+        log_testtracker_uuid(self.ad, self.current_test_name)
+        gutils.clear_logd_gnss_qxdm_log(self.ad)
+        gutils.get_baseband_and_gms_version(self.ad)
+        toggle_airplane_mode(self.ad.log, self.ad, new_state=False)
+        if gutils.is_wearable_btwifi(self.ad):
+            wutils.wifi_toggle_state(self.ad, True)
+            gutils.connect_to_wifi_network(self.ad,
+                                           self.ssid_map[self.pixel_lab_network[0]["SSID"]])
+        else:
+            wutils.wifi_toggle_state(self.ad, False)
+            gutils.set_mobile_data(self.ad, state=True)
+        if not verify_internet_connection(self.ad.log, self.ad, retries=3,
+                                          expected_state=True):
+            raise signals.TestFailure("Fail to connect to LTE network.")
+        # Once the device is rebooted, the xtra service will be alive again
+        # In order not to affect the supl case, disable it in setup_test.
+        if gutils.check_chipset_vendor_by_qualcomm(self.ad):
+            gutils.disable_qualcomm_orbit_assistance_data(self.ad)
+
+    def teardown_test(self):
+        if self.collect_logs:
+            gutils.stop_pixel_logger(self.ad)
+            stop_adb_tcpdump(self.ad)
+        if self.set_attenuator:
+            gutils.set_attenuator_gnss_signal(self.ad, self.attenuators,
+                                              self.default_gnss_signal_attenuation)
+        gutils.log_current_epoch_time(self.ad, "test_end_time")
+
+    def on_fail(self, test_name, begin_time):
+        if self.collect_logs:
+            self.ad.take_bug_report(test_name, begin_time)
+            gutils.get_gnss_qxdm_log(self.ad, self.qdsp6m_path)
+            self.get_brcm_gps_xml_to_sponge()
+            get_tcpdump_log(self.ad, test_name, begin_time)
+
+    def get_brcm_gps_xml_to_sponge(self):
+        # request from b/250506003 - to check the SUPL setting
+        if not gutils.check_chipset_vendor_by_qualcomm(self.ad):
+            self.ad.pull_files(gnss_defines.BCM_GPS_XML_PATH, self.ad.device_log_path)
+
+    def run_ttff(self, mode, criteria):
+        """Triggers TTFF.
+
+        Args:
+            mode: "cs", "ws" or "hs"
+            criteria: Criteria for the test.
+        """
+        return gutils.run_ttff(self.ad, mode, criteria, self.ttff_test_cycle,
+                               self.pixel_lab_location, self.collect_logs)
+
+    def supl_ttff_weak_gnss_signal(self, mode, criteria):
+        """Verify SUPL TTFF functionality under weak GNSS signal.
+
+        Args:
+            mode: "cs", "ws" or "hs"
+            criteria: Criteria for the test.
+        """
+        gutils.set_attenuator_gnss_signal(self.ad, self.attenuators,
+                                          self.weak_gnss_signal_attenuation)
+        self.run_ttff(mode, criteria)
+
+    def connect_to_wifi_with_mobile_data_off(self):
+        gutils.set_mobile_data(self.ad, False)
+        wutils.wifi_toggle_state(self.ad, True)
+        gutils.connect_to_wifi_network(self.ad, self.ssid_map[self.pixel_lab_network[0]["SSID"]])
+
+    def connect_to_wifi_with_airplane_mode_on(self):
+        toggle_airplane_mode(self.ad.log, self.ad, new_state=True)
+        wutils.wifi_toggle_state(self.ad, True)
+        gutils.connect_to_wifi_network(self.ad, self.ssid_map[self.pixel_lab_network[0]["SSID"]])
+
+    def check_position_mode(self, begin_time: int, mode: str):
+        logcat_results = self.ad.search_logcat(
+            matching_string="setting position_mode to", begin_time=begin_time)
+        return all([result["log_message"].split(" ")[-1] == mode for result in logcat_results])
+
+    def test_supl_capabilities(self):
+        """Verify SUPL capabilities.
+
+        Steps:
+            1. Root DUT.
+            2. Check SUPL capabilities.
+
+        Expected Results:
+            CAPABILITIES=0x37 which supports MSA + MSB.
+            CAPABILITIES=0x17 = ON_DEMAND_TIME | MSA | MSB | SCHEDULING
+        """
+        if not gutils.check_chipset_vendor_by_qualcomm(self.ad):
+            raise signals.TestSkip("Not Qualcomm chipset. Skip the test.")
+        capabilities_state = str(
+            self.ad.adb.shell(
+                "cat vendor/etc/gps.conf | grep CAPABILITIES")).split("=")[-1]
+        self.ad.log.info("SUPL capabilities - %s" % capabilities_state)
+
+        asserts.assert_true(capabilities_state in self.supl_capabilities,
+                            "Wrong default SUPL capabilities is set. Found %s, "
+                            "expected any of %r" % (capabilities_state,
+                                                    self.supl_capabilities))
+
+
+    def test_supl_ttff_cs(self):
+        """Verify SUPL functionality of TTFF Cold Start.
+
+        Steps:
+            1. Kill XTRA/LTO daemon to support SUPL only case.
+            2. SUPL TTFF Cold Start for 10 iteration.
+
+        Expected Results:
+            All SUPL TTFF Cold Start results should be less than
+            supl_cs_criteria.
+        """
+        self.run_ttff("cs", self.supl_cs_criteria)
+
+    def test_supl_ttff_ws(self):
+        """Verify SUPL functionality of TTFF Warm Start.
+
+        Steps:
+            1. Kill XTRA/LTO daemon to support SUPL only case.
+            2. SUPL TTFF Warm Start for 10 iteration.
+
+        Expected Results:
+            All SUPL TTFF Warm Start results should be less than
+            supl_ws_criteria.
+        """
+        self.run_ttff("ws", self.supl_ws_criteria)
+
+    def test_supl_ttff_hs(self):
+        """Verify SUPL functionality of TTFF Hot Start.
+
+        Steps:
+            1. Kill XTRA/LTO daemon to support SUPL only case.
+            2. SUPL TTFF Hot Start for 10 iteration.
+
+        Expected Results:
+            All SUPL TTFF Hot Start results should be less than
+            supl_hs_criteria.
+        """
+        self.run_ttff("hs", self.supl_hs_criteria)
+
+    def test_cs_ttff_supl_over_wifi_with_airplane_mode_on(self):
+        """ Test supl can works through wifi with airplane mode on
+
+        Test steps are executed in the following sequence.
+        - Turn on airplane mode
+        - Connect to wifi
+        - Run SUPL CS TTFF
+        """
+        self.only_brcm_device_runs_wifi_case()
+
+        self.connect_to_wifi_with_airplane_mode_on()
+
+        self.run_ttff(mode="cs", criteria=self.supl_cs_criteria)
+
+    def test_ws_ttff_supl_over_wifi_with_airplane_mode_on(self):
+        """ Test supl can works through wifi with airplane mode on
+
+        Test steps are executed in the following sequence.
+        - Turn on airplane mode
+        - Connect to wifi
+        - Run SUPL WS TTFF
+        """
+        self.only_brcm_device_runs_wifi_case()
+
+        self.connect_to_wifi_with_airplane_mode_on()
+
+        self.run_ttff("ws", self.supl_ws_criteria)
+
+    def test_hs_ttff_supl_over_wifi_with_airplane_mode_on(self):
+        """ Test supl can works through wifi with airplane mode on
+
+        Test steps are executed in the following sequence.
+        - Turn on airplane mode
+        - Connect to wifi
+        - Run SUPL WS TTFF
+        """
+        self.only_brcm_device_runs_wifi_case()
+
+        self.connect_to_wifi_with_airplane_mode_on()
+
+        self.run_ttff("hs", self.supl_ws_criteria)
+
+    def test_ttff_gla_on(self):
+        """ Test the turn on "Google Location Accuracy" in settings work or not.
+
+        Test steps are executed in the following sequence.
+        - Turn off airplane mode
+        - Connect to Cellular
+        - Turn off LTO/RTO
+        - Turn on SUPL
+        - Turn on GLA
+        - Run CS TTFF
+
+        Expected Results:
+        - The position mode must be "MS_BASED"
+        - The TTFF time should be less than 10 seconds
+        """
+        begin_time = get_current_epoch_time()
+        gutils.gla_mode(self.ad, True)
+
+        self.run_ttff("cs", self.supl_cs_criteria)
+        asserts.assert_true(self.check_position_mode(begin_time, "MS_BASED"),
+                                msg=f"Fail to enter the MS_BASED mode")
+
+    def test_ttff_gla_off(self):
+        """ Test the turn off "Google Location Accuracy" in settings work or not.
+
+        Test steps are executed in the following sequence.
+        - Turn off airplane mode
+        - Connect to Cellular
+        - Turn off LTO/RTO
+        - Turn on SUPL
+        - Turn off GLA
+        - Run CS TTFF
+
+        Expected Results:
+        - The position mode must be "standalone"
+        - The TTFF time must be between slower than supl_ws and faster than standalone_cs.
+        """
+        begin_time = get_current_epoch_time()
+        gutils.gla_mode(self.ad, False)
+
+        ttff_data = self.run_ttff("cs", self.standalone_cs_criteria)
+
+        asserts.assert_true(any(float(ttff_data[key].ttff_sec) > self.supl_ws_criteria
+                                for key in ttff_data.keys()),
+                            msg=f"One or more TTFF Cold Start are faster than \
+                            test criteria {self.supl_ws_criteria} seconds")
+
+        asserts.assert_true(self.check_position_mode(begin_time, "standalone"),
+                                msg=f"Fail to enter the standalone mode")
diff --git a/acts_tests/tests/google/gnss/GnssVendorFeaturesTest.py b/acts_tests/tests/google/gnss/GnssVendorFeaturesTest.py
new file mode 100644
index 0000000..e94626f
--- /dev/null
+++ b/acts_tests/tests/google/gnss/GnssVendorFeaturesTest.py
@@ -0,0 +1,290 @@
+import time
+
+from acts import asserts
+from acts import signals
+from acts.base_test import BaseTestClass
+from acts.utils import get_current_epoch_time
+from acts_contrib.test_utils.gnss import gnss_constant
+from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
+from acts_contrib.test_utils.gnss.testtracker_util import log_testtracker_uuid
+from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.tel import tel_logging_utils
+from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
+
+
+class GnssVendorFeaturesTest(BaseTestClass):
+    """Validate vendor specific features."""
+    def setup_class(self):
+        super().setup_class()
+        self.ad = self.android_devices[0]
+        req_params = ["pixel_lab_network", "default_gnss_signal_attenuation", "pixel_lab_location",
+                      "qdsp6m_path", "collect_logs", "ttff_test_cycle", "standalone_cs_criteria",
+                      "xtra_cs_criteria",  "xtra_ws_criteria", "xtra_hs_criteria",
+                      "set_attenuator"]
+        self.unpack_userparams(req_param_names=req_params)
+        # create hashmap for SSID
+        self.ssid_map = {}
+        for network in self.pixel_lab_network:
+            SSID = network["SSID"]
+            self.ssid_map[SSID] = network
+        self.init_device()
+
+    def init_device(self):
+        """Init GNSS test devices for vendor features suite."""
+        gutils._init_device(self.ad)
+        gutils.disable_supl_mode(self.ad)
+        gutils.enable_vendor_orbit_assistance_data(self.ad)
+
+    def setup_test(self):
+        gutils.log_current_epoch_time(self.ad, "test_start_time")
+        log_testtracker_uuid(self.ad, self.current_test_name)
+        gutils.clear_logd_gnss_qxdm_log(self.ad)
+        gutils.get_baseband_and_gms_version(self.ad)
+        toggle_airplane_mode(self.ad.log, self.ad, new_state=False)
+        if gutils.is_wearable_btwifi(self.ad):
+            wutils.wifi_toggle_state(self.ad, True)
+            gutils.connect_to_wifi_network(self.ad,
+                                           self.ssid_map[self.pixel_lab_network[0]["SSID"]])
+        else:
+            wutils.wifi_toggle_state(self.ad, False)
+            gutils.set_mobile_data(self.ad, state=True)
+        if not verify_internet_connection(self.ad.log, self.ad, retries=3,
+                                          expected_state=True):
+            raise signals.TestFailure("Fail to connect to internet.")
+
+    def teardown_test(self):
+        if self.collect_logs:
+            gutils.stop_pixel_logger(self.ad)
+            tel_logging_utils.stop_adb_tcpdump(self.ad)
+        if self.set_attenuator:
+            gutils.set_attenuator_gnss_signal(self.ad, self.attenuators,
+                                              self.default_gnss_signal_attenuation)
+        gutils.log_current_epoch_time(self.ad, "test_end_time")
+
+    def on_fail(self, test_name, begin_time):
+        if self.collect_logs:
+            self.ad.take_bug_report(test_name, begin_time)
+            gutils.get_gnss_qxdm_log(self.ad, self.qdsp6m_path)
+            tel_logging_utils.get_tcpdump_log(self.ad, test_name, begin_time)
+
+    def connect_to_wifi_with_airplane_mode_on(self):
+        self.ad.log.info("Turn airplane mode on")
+        toggle_airplane_mode(self.ad.log, self.ad, new_state=True)
+        wutils.wifi_toggle_state(self.ad, True)
+        gutils.connect_to_wifi_network(self.ad, self.ssid_map[self.pixel_lab_network[0]["SSID"]])
+
+    def ttff_with_assist(self, mode, criteria):
+        """Verify CS/WS TTFF functionality with Assist data.
+
+        Args:
+            mode: "csa" or "ws"
+            criteria: Criteria for the test.
+        """
+        begin_time = get_current_epoch_time()
+        gutils.process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
+        gutils.check_xtra_download(self.ad, begin_time)
+        self.ad.log.info("Turn airplane mode on")
+        toggle_airplane_mode(self.ad.log, self.ad, new_state=True)
+        gutils.start_gnss_by_gtw_gpstool(self.ad, True)
+        gutils.start_ttff_by_gtw_gpstool(self.ad, mode, iteration=self.ttff_test_cycle)
+        ttff_data = gutils.process_ttff_by_gtw_gpstool(self.ad, begin_time, self.pixel_lab_location)
+        result = gutils.check_ttff_data(self.ad, ttff_data, mode, criteria)
+        asserts.assert_true(
+            result, "TTFF %s fails to reach designated criteria of %d "
+                    "seconds." % (gnss_constant.TTFF_MODE.get(mode), criteria))
+
+    def test_xtra_ttff_cs_mobile_data(self):
+        """Verify XTRA/LTO functionality of TTFF Cold Start with mobile data.
+
+        Steps:
+            1. TTFF Cold Start for 10 iteration.
+
+        Expected Results:
+            XTRA/LTO TTFF Cold Start results should be within xtra_cs_criteria.
+        """
+        gutils.run_ttff(self.ad, mode="cs", criteria=self.xtra_cs_criteria,
+                        test_cycle=self.ttff_test_cycle, base_lat_long=self.pixel_lab_location,
+                        collect_logs=self.collect_logs)
+
+    def test_xtra_ttff_ws_mobile_data(self):
+        """Verify XTRA/LTO functionality of TTFF Warm Start with mobile data.
+
+        Steps:
+            1. TTFF Warm Start for 10 iteration.
+
+        Expected Results:
+            XTRA/LTO TTFF Warm Start results should be within xtra_ws_criteria.
+        """
+        gutils.run_ttff(self.ad, mode="ws", criteria=self.xtra_ws_criteria,
+                        test_cycle=self.ttff_test_cycle, base_lat_long=self.pixel_lab_location,
+                        collect_logs=self.collect_logs)
+
+    def test_xtra_ttff_hs_mobile_data(self):
+        """Verify XTRA/LTO functionality of TTFF Hot Start with mobile data.
+
+        Steps:
+            1. TTFF Hot Start for 10 iteration.
+
+        Expected Results:
+            XTRA/LTO TTFF Hot Start results should be within xtra_hs_criteria.
+        """
+        gutils.run_ttff(self.ad, mode="hs", criteria=self.xtra_hs_criteria,
+                        test_cycle=self.ttff_test_cycle, base_lat_long=self.pixel_lab_location,
+                        collect_logs=self.collect_logs)
+
+    def test_xtra_ttff_cs_wifi(self):
+        """Verify XTRA/LTO functionality of TTFF Cold Start with WiFi.
+
+        Steps:
+            1. Turn airplane mode on.
+            2. Connect to WiFi.
+            3. TTFF Cold Start for 10 iteration.
+
+        Expected Results:
+            XTRA/LTO TTFF Cold Start results should be within
+            xtra_cs_criteria.
+        """
+        self.connect_to_wifi_with_airplane_mode_on()
+        gutils.run_ttff(self.ad, mode="cs", criteria=self.xtra_cs_criteria,
+                        test_cycle=self.ttff_test_cycle, base_lat_long=self.pixel_lab_location,
+                        collect_logs=self.collect_logs)
+
+    def test_xtra_ttff_ws_wifi(self):
+        """Verify XTRA/LTO functionality of TTFF Warm Start with WiFi.
+
+        Steps:
+            1. Turn airplane mode on.
+            2. Connect to WiFi.
+            3. TTFF Warm Start for 10 iteration.
+
+        Expected Results:
+            XTRA/LTO TTFF Warm Start results should be within xtra_ws_criteria.
+        """
+        self.connect_to_wifi_with_airplane_mode_on()
+        gutils.run_ttff(self.ad, mode="ws", criteria=self.xtra_ws_criteria,
+                        test_cycle=self.ttff_test_cycle, base_lat_long=self.pixel_lab_location,
+                        collect_logs=self.collect_logs)
+
+    def test_xtra_ttff_hs_wifi(self):
+        """Verify XTRA/LTO functionality of TTFF Hot Start with WiFi.
+
+        Steps:
+            1. Turn airplane mode on.
+            2. Connect to WiFi.
+            3. TTFF Hot Start for 10 iteration.
+
+        Expected Results:
+            XTRA/LTO TTFF Hot Start results should be within xtra_hs_criteria.
+        """
+        self.connect_to_wifi_with_airplane_mode_on()
+        gutils.run_ttff(self.ad, mode="hs", criteria=self.xtra_hs_criteria,
+                        test_cycle=self.ttff_test_cycle, base_lat_long=self.pixel_lab_location,
+                        collect_logs=self.collect_logs)
+
+    def test_xtra_download_mobile_data(self):
+        """Verify XTRA/LTO data could be downloaded via mobile data.
+
+        Steps:
+            1. Delete all GNSS aiding data.
+            2. Get location fixed.
+            3. Verify whether XTRA/LTO is downloaded and injected.
+            4. Repeat Step 1. to Step 3. for 5 times.
+
+        Expected Results:
+            XTRA/LTO data is properly downloaded and injected via mobile data.
+        """
+        mobile_xtra_result_all = []
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
+        for i in range(1, 6):
+            begin_time = get_current_epoch_time()
+            gutils.process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
+            time.sleep(5)
+            gutils.start_gnss_by_gtw_gpstool(self.ad, False)
+            mobile_xtra_result = gutils.check_xtra_download(self.ad, begin_time)
+            self.ad.log.info("Iteration %d => %s" % (i, mobile_xtra_result))
+            mobile_xtra_result_all.append(mobile_xtra_result)
+        asserts.assert_true(all(mobile_xtra_result_all),
+                            "Fail to Download and Inject XTRA/LTO File.")
+
+    def test_xtra_download_wifi(self):
+        """Verify XTRA/LTO data could be downloaded via WiFi.
+
+        Steps:
+            1. Connect to WiFi.
+            2. Delete all GNSS aiding data.
+            3. Get location fixed.
+            4. Verify whether XTRA/LTO is downloaded and injected.
+            5. Repeat Step 2. to Step 4. for 5 times.
+
+        Expected Results:
+            XTRA data is properly downloaded and injected via WiFi.
+        """
+        wifi_xtra_result_all = []
+        gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
+        self.connect_to_wifi_with_airplane_mode_on()
+        for i in range(1, 6):
+            begin_time = get_current_epoch_time()
+            gutils.process_gnss_by_gtw_gpstool(self.ad, self.standalone_cs_criteria)
+            time.sleep(5)
+            gutils.start_gnss_by_gtw_gpstool(self.ad, False)
+            wifi_xtra_result = gutils.check_xtra_download(self.ad, begin_time)
+            wifi_xtra_result_all.append(wifi_xtra_result)
+            self.ad.log.info("Iteration %d => %s" % (i, wifi_xtra_result))
+        asserts.assert_true(all(wifi_xtra_result_all),
+                            "Fail to Download and Inject XTRA/LTO File.")
+
+    def test_lto_download_after_reboot(self):
+        """Verify LTO data could be downloaded and injected after device reboot.
+
+        Steps:
+            1. Reboot device.
+            2. Verify whether LTO is auto downloaded and injected without trigger GPS.
+            3. Repeat Step 1 to Step 2 for 5 times.
+
+        Expected Results:
+            LTO data is properly downloaded and injected at the first time tether to phone.
+        """
+        reboot_lto_test_results_all = []
+        for times in range(1, 6):
+            gutils.delete_lto_file(self.ad)
+            gutils.reboot(self.ad)
+            gutils.start_qxdm_and_tcpdump_log(self.ad, self.collect_logs)
+            # Wait 20 seconds for boot busy and lto auto-download time
+            time.sleep(20)
+            begin_time = get_current_epoch_time()
+            reboot_lto_test_result = gutils.check_xtra_download(self.ad, begin_time)
+            self.ad.log.info("Iteration %d => %s" % (times, reboot_lto_test_result))
+            reboot_lto_test_results_all.append(reboot_lto_test_result)
+            gutils.stop_pixel_logger(self.ad)
+            tel_logging_utils.stop_adb_tcpdump(self.ad)
+        asserts.assert_true(all(reboot_lto_test_results_all),
+                                "Fail to Download and Inject LTO File.")
+
+    def test_ws_with_assist(self):
+        """Verify Warm Start functionality with existed LTO data.
+
+        Steps:
+            2. Make LTO is downloaded.
+            3. Turn on AirPlane mode to make sure there's no network connection.
+            4. TTFF Warm Start with Assist for 10 iteration.
+
+        Expected Results:
+            All TTFF Warm Start with Assist results should be within
+            xtra_ws_criteria.
+        """
+        self.ttff_with_assist("ws", self.xtra_ws_criteria)
+
+    def test_cs_with_assist(self):
+        """Verify Cold Start functionality with existed LTO data.
+
+        Steps:
+            2. Make sure LTO is downloaded.
+            3. Turn on AirPlane mode to make sure there's no network connection.
+            4. TTFF Cold Start with Assist for 10 iteration.
+
+        Expected Results:
+            All TTFF Cold Start with Assist results should be within
+            standalone_cs_criteria.
+        """
+        self.ttff_with_assist("csa", self.standalone_cs_criteria)
diff --git a/acts_tests/tests/google/gnss/GnssWearableTetherFunctionTest.py b/acts_tests/tests/google/gnss/GnssWearableTetherFunctionTest.py
index 87a233d..a90a01f 100644
--- a/acts_tests/tests/google/gnss/GnssWearableTetherFunctionTest.py
+++ b/acts_tests/tests/google/gnss/GnssWearableTetherFunctionTest.py
@@ -14,16 +14,16 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 import time
-import os
+import statistics
 
 from acts import asserts
 from acts import signals
 from acts.base_test import BaseTestClass
-from acts.test_decorators import test_tracker_info
+from acts_contrib.test_utils.gnss.testtracker_util import log_testtracker_uuid
 from acts_contrib.test_utils.gnss import gnss_test_utils as gutils
-from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 from acts_contrib.test_utils.tel import tel_logging_utils as tutils
 from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts.utils import get_current_epoch_time
 from acts_contrib.test_utils.gnss.gnss_test_utils import delete_lto_file, pair_to_wearable
 from acts_contrib.test_utils.gnss.gnss_test_utils import process_gnss_by_gtw_gpstool
@@ -43,7 +43,7 @@
                       "flp_ttff_max_threshold", "pixel_lab_location",
                       "flp_ttff_cycle", "default_gnss_signal_attenuation",
                       "flp_waiting_time", "tracking_test_time",
-                      "fast_start_criteria" ]
+                      "far_start_criteria", "ttff_test_cycle"]
         self.unpack_userparams(req_param_names=req_params)
         # create hashmap for SSID
         self.ssid_map = {}
@@ -56,30 +56,34 @@
         gutils._init_device(self.watch)
         pair_to_wearable(self.watch, self.phone)
 
+    def teardown_class(self):
+        super().teardown_class()
+        gutils.reboot(self.phone)
+
     def setup_test(self):
+        gutils.log_current_epoch_time(self.watch, "test_start_time")
+        log_testtracker_uuid(self.watch, self.current_test_name)
         gutils.get_baseband_and_gms_version(self.watch)
         gutils.clear_logd_gnss_qxdm_log(self.watch)
         gutils.clear_logd_gnss_qxdm_log(self.phone)
         gutils.set_attenuator_gnss_signal(self.watch, self.attenuators,
-                                       self.default_gnss_signal_attenuation)
-        if not gutils.is_mobile_data_on(self.watch):
-            gutils.set_mobile_data(self.watch, True)
-        # TODO (b/202101058:chenstanley): Need to double check how to disable wifi successfully in wearable projects.
-        if gutils.is_wearable_btwifi(self.watch):
-            wutils.wifi_toggle_state(self.watch, True)
-            gutils.connect_to_wifi_network(
-                self.watch, self.ssid_map[self.pixel_lab_network[0]["SSID"]])
-        if not verify_internet_connection(self.watch.log, self.watch, retries=3,
+                                          self.default_gnss_signal_attenuation)
+        if not verify_internet_connection(self.watch.log, self.watch, retries=5,
                                           expected_state=True):
-            raise signals.TestFailure("Fail to connect to LTE or WiFi network.")
-        if not gutils.is_bluetooth_connected(self.watch, self.phone):
-            gutils.pair_to_wearable(self.phone, self.watch)
+            time.sleep(60)
+            if not verify_internet_connection(self.watch.log, self.watch, retries=3,
+                                              expected_state=True):
+                raise signals.TestFailure("Fail to connect to LTE network.")
 
     def teardown_test(self):
         gutils.stop_pixel_logger(self.watch)
         tutils.stop_adb_tcpdump(self.watch)
         gutils.set_attenuator_gnss_signal(self.watch, self.attenuators,
                                        self.default_gnss_signal_attenuation)
+        if self.watch.droid.connectivityCheckAirplaneMode():
+            self.watch.log.info("Force airplane mode off")
+            toggle_airplane_mode(self.watch.log, self.watch, new_state=False)
+        gutils.log_current_epoch_time(self.watch, "test_end_time")
 
     def on_fail(self, test_name, begin_time):
         self.watch.take_bug_report(test_name, begin_time)
@@ -93,57 +97,44 @@
 
     def flp_ttff(self, mode, criteria, location):
         self.start_qxdm_and_tcpdump_log()
-        start_gnss_by_gtw_gpstool(self.phone, True, type="FLP")
+        start_gnss_by_gtw_gpstool(self.phone, True, api_type="FLP")
         time.sleep(self.flp_waiting_time)
         self.watch.unlock_screen(password=None)
         begin_time = get_current_epoch_time()
         process_gnss_by_gtw_gpstool(
-            self.watch, self.standalone_cs_criteria, type="flp")
+            self.watch, self.standalone_cs_criteria, api_type="flp")
         gutils.start_ttff_by_gtw_gpstool(
             self.watch, mode, iteration=self.flp_ttff_cycle)
         results = gutils.process_ttff_by_gtw_gpstool(
-            self.watch, begin_time, location, type="flp")
+            self.watch, begin_time, location, api_type="flp")
         gutils.check_ttff_data(self.watch, results, mode, criteria)
         self.check_location_from_phone()
-        start_gnss_by_gtw_gpstool(self.phone, False, type="FLP")
+        start_gnss_by_gtw_gpstool(self.phone, False, api_type="FLP")
 
     def check_location_from_phone(self):
         watch_file = check_tracking_file(self.watch)
         phone_file = check_tracking_file(self.phone)
         return gutils.compare_watch_phone_location(self, watch_file, phone_file)
 
-    """ Test Cases """
+    def run_ttff_via_gtw_gpstool(self, mode, criteria):
+        """Run GNSS TTFF test with selected mode and parse the results.
 
-    @test_tracker_info(uuid="2c62183a-4354-4efc-92f2-84580cbd3398")
-    def test_lto_download_after_reboot(self):
-        """Verify LTO data could be downloaded and injected after device reboot.
-
-        Steps:
-            1. Reboot device.
-            2. Verify whether LTO is auto downloaded and injected without trigger GPS.
-            3. Repeat Step 1 to Step 2 for 5 times.
-
-        Expected Results:
-            LTO data is properly downloaded and injected at the first time tether to phone.
+        Args:
+            mode: "cs", "ws" or "hs"
+            criteria: Criteria for the TTFF.
         """
-        reboot_lto_test_results_all = []
-        gutils.disable_supl_mode(self.watch)
-        for times in range(1, 6):
-            delete_lto_file(self.watch)
-            gutils.reboot(self.watch)
-            self.start_qxdm_and_tcpdump_log()
-            # Wait 20 seconds for boot busy and lto auto-download time
-            time.sleep(20)
-            begin_time = get_current_epoch_time()
-            reboot_lto_test_result = gutils.check_xtra_download(self.watch, begin_time)
-            self.watch.log.info("Iteration %d => %s" % (times, reboot_lto_test_result))
-            reboot_lto_test_results_all.append(reboot_lto_test_result)
-            gutils.stop_pixel_logger(self.watch)
-            tutils.stop_adb_tcpdump(self.watch)
-        asserts.assert_true(all(reboot_lto_test_results_all),
-                                "Fail to Download and Inject LTO File.")
+        begin_time = get_current_epoch_time()
+        gutils.process_gnss_by_gtw_gpstool(self.watch, self.standalone_cs_criteria, clear_data=False)
+        gutils.start_ttff_by_gtw_gpstool(self.watch, mode, self.ttff_test_cycle)
+        ttff_data = gutils.process_ttff_by_gtw_gpstool(
+            self.watch, begin_time, self.pixel_lab_location)
+        result = gutils.check_ttff_data(
+            self.watch, ttff_data, self.ttff_mode.get(mode), criteria)
+        asserts.assert_true(
+            result, "TTFF %s fails to reach designated criteria of %d "
+                    "seconds." % (self.ttff_mode.get(mode), criteria))
 
-    @test_tracker_info(uuid="7ed596df-df71-42ca-bdb3-69a3cad81963")
+    """ Test Cases """
     def test_flp_ttff_cs(self):
         """Verify FLP TTFF Cold Start while tether with phone.
 
@@ -159,7 +150,6 @@
         """
         self.flp_ttff("cs", self.flp_ttff_max_threshold, self.pixel_lab_location)
 
-    @test_tracker_info(uuid="de19617c-1f03-4077-99af-542b300ab4ed")
     def test_flp_ttff_ws(self):
         """Verify FLP TTFF Warm Start while tether with phone.
 
@@ -175,7 +165,6 @@
         """
         self.flp_ttff("ws", self.flp_ttff_max_threshold, self.pixel_lab_location)
 
-    @test_tracker_info(uuid="c58c90ae-9f4a-4619-a9f8-f2f98c930008")
     def test_flp_ttff_hs(self):
         """Verify FLP TTFF Hot Start while tether with phone.
 
@@ -191,7 +180,6 @@
         """
         self.flp_ttff("hs", self.flp_ttff_max_threshold, self.pixel_lab_location)
 
-    @test_tracker_info(uuid="ca955ad3-e2eb-4fde-af2b-3e19abe47792")
     def test_tracking_during_bt_disconnect_resume(self):
         """Verify tracking is correct during Bluetooth disconnect and resume.
 
@@ -208,17 +196,17 @@
             2. Tracking results should be within pixel_lab_location criteria.
         """
         self.start_qxdm_and_tcpdump_log()
-        for i in range(1, 6):
+        for i in range(1, 4):
             if not self.watch.droid.bluetoothCheckState():
                 self.watch.droid.bluetoothToggleState(True)
                 self.watch.log.info("Turn Bluetooth on")
-                self.watch.log.info("Wait 1 min for Bluetooth auto re-connect")
-                time.sleep(60)
-            if not gutils.is_bluetooth_connect(self.watch, self.phone):
+                self.watch.log.info("Wait 40s for Bluetooth auto re-connect")
+                time.sleep(40)
+            if not gutils.is_bluetooth_connected(self.watch, self.phone):
                 raise signals.TestFailure("Fail to connect to device via Bluetooth.")
-            start_gnss_by_gtw_gpstool(self.phone, True, type="FLP")
+            start_gnss_by_gtw_gpstool(self.phone, True, api_type="FLP")
             time.sleep(self.flp_waiting_time)
-            start_gnss_by_gtw_gpstool(self.watch, True, type="FLP")
+            start_gnss_by_gtw_gpstool(self.watch, True, api_type="FLP")
             time.sleep(self.flp_waiting_time)
             self.watch.log.info("Wait 1 min for tracking")
             time.sleep(self.tracking_test_time)
@@ -230,12 +218,11 @@
             time.sleep(self.tracking_test_time)
             if self.check_location_from_phone():
                 raise signals.TestError("Watch should not use phone location")
-            gutils.parse_gtw_gpstool_log(self.watch, self.pixel_lab_location, type="FLP")
-            start_gnss_by_gtw_gpstool(self.phone, False, type="FLP")
+            gutils.parse_gtw_gpstool_log(self.watch, self.pixel_lab_location, api_type="FLP")
+            start_gnss_by_gtw_gpstool(self.phone, False, api_type="FLP")
 
-    @test_tracker_info(uuid="654a8f1b-f9c6-433e-a21f-59224cce822e")
-    def test_fast_start_first_fix_and_ttff(self):
-        """Verify first fix and TTFF of Fast Start (Warm Start v4) within the criteria
+    def test_oobe_first_fix(self):
+        """Verify first fix after OOBE pairing within the criteria
 
         Steps:
             1. Pair watch to phone during OOBE.
@@ -244,29 +231,78 @@
             4. Enable AirPlane mode to untether to phone.
             5. Open GPSTool to get first fix in LTO and UTC time injected.
             6. Repeat Step1 ~ Step5 for 5 times.
-            7. After Step6, Warm Start TTFF for 10 iterations.
 
         Expected Results:
-            1. First fix should be within fast_start_threshold.
-            2. TTFF should be within fast_start_threshold.
+            1. First fix should be within far_start_threshold.
         """
-        for i in range(1,6):
-            self.watch.log.info("First fix of Fast Start - attempts %s" % i)
+        oobe_results_all = []
+        for i in range(1,4):
+            self.watch.log.info("First fix after OOBE pairing - attempts %s" % i)
             pair_to_wearable(self.watch, self.phone)
-            gutils.enable_framework_log(self.watch)
             self.start_qxdm_and_tcpdump_log()
             begin_time = get_current_epoch_time()
             gutils.check_xtra_download(self.watch, begin_time)
             gutils.check_inject_time(self.watch)
             self.watch.log.info("Turn airplane mode on")
-            self.watch.droid.connectivityToggleAirplaneMode(True)
-            self.watch.unlock_screen(password=None)
-            gutils.process_gnss_by_gtw_gpstool(
-                self.watch, self.fast_start_criteria, clear_data=False)
-        gutils.start_ttff_by_gtw_gpstool(
-            self.watch, ttff_mode="ws", iteration=self.ttff_test_cycle)
-        ttff_data = gutils.process_ttff_by_gtw_gpstool(self.watch, begin_time,
-                                                self.pixel_lab_location)
-        result = gutils.check_ttff_data(self.watch, ttff_data, self.ttff_mode.get("ws"),
-                                 criteria=self.fast_start_criteria)
-        asserts.assert_true(result, "TTFF fails to reach designated criteria")
+            # self.watch.droid.connectivityToggleAirplaneMode(True)
+            toggle_airplane_mode(self.watch.log, self.watch, new_state=True)
+            oobe_results = gutils.process_gnss(
+                self.watch, self.far_start_criteria, clear_data=False)
+            oobe_results_all.append(oobe_results)
+        self.watch.log.info(f"TestResult Max_OOBE_First_Fix {max(oobe_results_all)}")
+        self.watch.log.info(f"TestResult Avg_OOBE_First_Fix {statistics.mean(oobe_results_all)}")
+        # self.watch.droid.connectivityToggleAirplaneMode(False)
+        toggle_airplane_mode(self.watch.log, self.watch, new_state=False)
+        self.watch.log.info("Turn airplane mode off")
+
+    def test_oobe_first_fix_with_network_connection(self):
+        """Verify first fix after OOBE pairing within the criteria
+
+        Steps:
+            1. Pair watch to phone during OOBE.
+            2. Ensure LTO file download in watch.
+            3. Ensure UTC time inject in watch.
+            4. Turn off Bluetooth to untether to phone.
+            5. Open GPSTool to get first fix in LTO and UTC time injected.
+            6. Repeat Step1 ~ Step5 for 5 times.
+
+        Expected Results:
+            1. First fix should be within far_start_threshold.
+        """
+        oobe_results_all = []
+        for i in range(1,4):
+            self.watch.log.info("First fix after OOBE pairing - attempts %s" % i)
+            pair_to_wearable(self.watch, self.phone)
+            self.start_qxdm_and_tcpdump_log()
+            begin_time = get_current_epoch_time()
+            gutils.check_xtra_download(self.watch, begin_time)
+            gutils.check_inject_time(self.watch)
+            self.watch.log.info("Turn off Bluetooth to disconnect to phone")
+            self.watch.droid.bluetoothToggleState(False)
+            oobe_results = gutils.process_gnss(
+                self.watch, self.far_start_criteria, clear_data=False)
+            oobe_results_all.append(oobe_results)
+        self.watch.log.info(f"TestResult Max_OOBE_First_Fix {max(oobe_results_all)}")
+        self.watch.log.info(f"TestResult Avg_OOBE_First_Fix {statistics.mean(oobe_results_all)}")
+
+    def test_far_start_ttff(self):
+        """Verify Far Start (Warm Start v4) TTFF within the criteria
+
+        Steps:
+            1. Pair watch to phone during OOBE.
+            2. Ensure LTO file download in watch.
+            3. Ensure UTC time inject in watch.
+            4. Enable AirPlane mode to untether to phone.
+            5. TTFF Warm Start for 10 iteration.
+
+        Expected Results:
+            1. TTFF should be within far_start_threshold.
+        """
+        pair_to_wearable(self.watch, self.phone)
+        self.start_qxdm_and_tcpdump_log()
+        begin_time = get_current_epoch_time()
+        gutils.check_xtra_download(self.watch, begin_time)
+        gutils.check_inject_time(self.watch)
+        self.watch.log.info("Turn airplane mode on")
+        toggle_airplane_mode(self.watch.log, self.watch, new_state=True)
+        self.run_ttff_via_gtw_gpstool("ws", self.far_start_criteria)
diff --git a/acts_tests/tests/google/gnss/LabGnssPowerSweepTest.py b/acts_tests/tests/google/gnss/LabGnssPowerSweepTest.py
new file mode 100644
index 0000000..f641cc4
--- /dev/null
+++ b/acts_tests/tests/google/gnss/LabGnssPowerSweepTest.py
@@ -0,0 +1,324 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2020 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT 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_contrib.test_utils.gnss.GnssBlankingBase import GnssBlankingBase
+from collections import namedtuple
+from acts_contrib.test_utils.gnss.LabTtffTestBase import LabTtffTestBase
+from acts_contrib.test_utils.gnss.gnss_test_utils import detect_crash_during_tracking, gnss_tracking_via_gtw_gpstool, \
+                                    start_gnss_by_gtw_gpstool, process_ttff_by_gtw_gpstool, calculate_position_error
+from acts.context import get_current_context
+from acts.utils import get_current_epoch_time
+from time import sleep
+import csv
+import matplotlib.pyplot as plt
+from mpl_toolkits.mplot3d.axes3d import Axes3D
+import statistics
+
+
+class LabGnssPowerSweepTest(GnssBlankingBase):
+
+    def gnss_plot_2D_result(self, position_error):
+        """Plot 2D position error result
+        """
+        x_axis = []
+        y_axis = []
+        z_axis = []
+        for key in position_error:
+            tmp = key.split('_')
+            l1_pwr = float(tmp[1])
+            l5_pwr = float(tmp[3])
+            position_error_value = position_error[key]
+            x_axis.append(l1_pwr)
+            y_axis.append(l5_pwr)
+            z_axis.append(position_error_value)
+
+        fig = plt.figure(figsize=(12, 7))
+        ax = plt.axes(projection='3d')
+        ax.scatter(x_axis, y_axis, z_axis)
+        plt.title("Z axis Position Error", fontsize=12)
+        plt.xlabel("L1 PWR (dBm)", fontsize=12)
+        plt.ylabel("L5 PWR (dBm)", fontsize=12)
+        plt.show()
+        path_name = os.path.join(self.gnss_log_path, 'result.png')
+        plt.savefig(path_name)
+
+    def gnss_wait_for_ephemeris_download(self):
+        """Launch GTW GPSTool and Clear all GNSS aiding data
+           Start GNSS tracking on GTW_GPSTool.
+           Wait for "first_wait" at simulator power = "power_level" to download Ephemeris
+        """
+        first_wait = self.user_params.get('first_wait', 300)
+        LabTtffTestBase.start_set_gnss_power(self)
+        self.start_gnss_and_wait(first_wait)
+
+    def gnss_check_fix(self, json_tag):
+        """Launch GTW GPSTool and check position fix or not
+          Returns:
+            True : Can fix within 120 sec
+            False
+        """
+        # Check Latitude for fix
+        self.dut.log.info("Restart GTW GPSTool in gnss_check_fix")
+        start_gnss_by_gtw_gpstool(self.dut, state=True)
+        begin_time = get_current_epoch_time()
+        if not self.dut.is_adb_logcat_on:
+            self.dut.start_adb_logcat()
+        while True:
+            if get_current_epoch_time() - begin_time >= 120000:
+                self.dut.log.info("Location fix timeout in gnss_check_fix")
+                start_gnss_by_gtw_gpstool(self.dut, state=False)
+                json_tag = json_tag + '_gnss_check_fix_timeout'
+                self.dut.cat_adb_log(tag=json_tag,
+                                     begin_time=begin_time,
+                                     end_time=None,
+                                     dest_path=self.gnss_log_path)
+                return False
+            sleep(1)
+            logcat_results = self.dut.search_logcat("Latitude", begin_time)
+            if logcat_results:
+                self.dut.log.info("Location fix successfully in gnss_check_fix")
+                json_tag = json_tag + '_gnss_check_fix_success'
+                self.dut.cat_adb_log(tag=json_tag,
+                                     begin_time=begin_time,
+                                     end_time=None,
+                                     dest_path=self.gnss_log_path)
+                return True
+
+    def gnss_check_l5_engaging(self, json_tag):
+        """check L5 engaging
+          Returns:
+            True : L5 engaged
+            False
+        """
+        # Check L5 engaging rate
+        begin_time = get_current_epoch_time()
+        if not self.dut.is_adb_logcat_on:
+            self.dut.start_adb_logcat()
+        while True:
+            if get_current_epoch_time() - begin_time >= 120000:
+                self.dut.log.info(
+                    "L5 engaging timeout in gnss_check_l5_engaging")
+                start_gnss_by_gtw_gpstool(self.dut, state=False)
+                json_tag = json_tag + '_gnss_check_l5_engaging_timeout'
+                self.dut.cat_adb_log(tag=json_tag,
+                                     begin_time=begin_time,
+                                     end_time=None,
+                                     dest_path=self.gnss_log_path)
+                return False
+            sleep(1)
+            logcat_results = self.dut.search_logcat("L5 engaging rate:",
+                                                    begin_time)
+            if logcat_results:
+                start_idx = logcat_results[-1]['log_message'].find(
+                    "L5 engaging rate:")
+                tmp = logcat_results[-1]['log_message'][(start_idx + 18):]
+                l5_engaging_rate = float(tmp.strip('%'))
+
+                if l5_engaging_rate != 0:
+                    self.dut.log.info("L5 engaged")
+                    json_tag = json_tag + '_gnss_check_l5_engaging_success'
+                    self.dut.cat_adb_log(tag=json_tag,
+                                         begin_time=begin_time,
+                                         end_time=None,
+                                         dest_path=self.gnss_log_path)
+                    return True
+
+    def gnss_check_position_error(self, json_tag):
+        """check position error
+          Returns:
+            position error average value
+        """
+        average_position_error_count = 60
+        position_error_all = []
+        hacc_all = []
+        default_position_error_mean = 6666
+        default_position_error_std = 6666
+        default_hacc_mean = 6666
+        default_hacc_std = 6666
+        idx = 0
+        begin_time = get_current_epoch_time()
+        if not self.dut.is_adb_logcat_on:
+            self.dut.start_adb_logcat()
+        while True:
+            if get_current_epoch_time() - begin_time >= 120000:
+                self.dut.log.info(
+                    "Position error calculation timeout in gnss_check_position_error"
+                )
+                start_gnss_by_gtw_gpstool(self.dut, state=False)
+                json_tag = json_tag + '_gnss_check_position_error_timeout'
+                self.dut.cat_adb_log(tag=json_tag,
+                                     begin_time=begin_time,
+                                     end_time=None,
+                                     dest_path=self.gnss_log_path)
+                return default_position_error_mean, default_position_error_std, default_hacc_mean, default_hacc_std
+            sleep(1)
+            gnss_results = self.dut.search_logcat("GPSService: Check item",
+                                                  begin_time)
+            if gnss_results:
+                self.dut.log.info(gnss_results[-1]["log_message"])
+                gnss_location_log = \
+                    gnss_results[-1]["log_message"].split()
+                ttff_lat = float(gnss_location_log[8].split("=")[-1].strip(","))
+                ttff_lon = float(gnss_location_log[9].split("=")[-1].strip(","))
+                loc_time = int(gnss_location_log[10].split("=")[-1].strip(","))
+                ttff_haccu = float(
+                    gnss_location_log[11].split("=")[-1].strip(","))
+                hacc_all.append(ttff_haccu)
+                position_error = calculate_position_error(
+                    ttff_lat, ttff_lon, self.simulator_location)
+                position_error_all.append(abs(position_error))
+                idx = idx + 1
+                if idx >= average_position_error_count:
+                    position_error_mean = statistics.mean(position_error_all)
+                    position_error_std = statistics.stdev(position_error_all)
+                    hacc_mean = statistics.mean(hacc_all)
+                    hacc_std = statistics.stdev(hacc_all)
+                    json_tag = json_tag + '_gnss_check_position_error_success'
+                    self.dut.cat_adb_log(tag=json_tag,
+                                         begin_time=begin_time,
+                                         end_time=None,
+                                         dest_path=self.gnss_log_path)
+                    return position_error_mean, position_error_std, hacc_mean, hacc_std
+
+    def gnss_tracking_L5_position_error_capture(self, json_tag):
+        """Capture position error after L5 engaged
+        Args:
+        Returns:
+            Position error with L5
+        """
+        self.dut.log.info('Start gnss_tracking_L5_position_error_capture')
+        fixed = self.gnss_check_fix(json_tag)
+        if fixed:
+            l5_engaged = self.gnss_check_l5_engaging(json_tag)
+            if l5_engaged:
+                position_error_mean, position_error_std, hacc_mean, hacc_std = self.gnss_check_position_error(
+                    json_tag)
+                start_gnss_by_gtw_gpstool(self.dut, state=False)
+            else:
+                position_error_mean = 8888
+                position_error_std = 8888
+                hacc_mean = 8888
+                hacc_std = 8888
+        else:
+            position_error_mean = 9999
+            position_error_std = 9999
+            hacc_mean = 9999
+            hacc_std = 9999
+            self.position_fix_timeout_cnt = self.position_fix_timeout_cnt + 1
+
+            if self.position_fix_timeout_cnt > (self.l1_sweep_cnt / 2):
+                self.l1_sensitivity_point = self.current_l1_pwr
+
+        return position_error_mean, position_error_std, hacc_mean, hacc_std
+
+    def gnss_power_tracking_loop(self):
+        """Launch GTW GPSTool and Clear all GNSS aiding data
+           Start GNSS tracking on GTW_GPSTool.
+
+        Args:
+
+        Returns:
+            True: First fix TTFF are within criteria.
+            False: First fix TTFF exceed criteria.
+        """
+        test_period = 60
+        type = 'gnss'
+        start_time = get_current_epoch_time()
+        start_gnss_by_gtw_gpstool(self.dut, state=True, type=type)
+        while get_current_epoch_time() - start_time < test_period * 1000:
+            detect_crash_during_tracking(self.dut, start_time, type)
+        stop_time = get_current_epoch_time()
+
+        return start_time, stop_time
+
+    def parse_tracking_log_cat(self, log_dir):
+        self.log.warning(f'Parsing log cat {log_dir} results into dataframe!')
+
+    def check_l5_points(self, gnss_pwr_swp):
+        cnt = 0
+        for kk in range(len(gnss_pwr_swp[1])):
+            if gnss_pwr_swp[1][kk][0] == gnss_pwr_swp[1][kk + 1][0]:
+                cnt = cnt + 1
+            else:
+                return cnt
+
+    def test_tracking_power_sweep(self):
+        # Create log file path
+        full_output_path = get_current_context().get_full_output_path()
+        self.gnss_log_path = os.path.join(full_output_path, '')
+        os.makedirs(self.gnss_log_path, exist_ok=True)
+        self.log.debug(f'Create log path: {self.gnss_log_path}')
+        csv_path = self.gnss_log_path + 'L1_L5_2D_search_result.csv'
+        csvfile = open(csv_path, 'w')
+        writer = csv.writer(csvfile)
+        writer.writerow([
+            "csv_result_tag", "position_error_mean", "position_error_std",
+            "hacc_mean", "hacc_std"
+        ])
+        # for L1 position fix early termination
+        self.l1_sensitivity_point = -999
+        self.enable_early_terminate = 1
+        self.position_fix_timeout_cnt = 0
+        self.current_l1_pwr = 0
+        self.l1_sweep_cnt = 0
+
+        self.gnss_wait_for_ephemeris_download()
+        l1_cable_loss = self.gnss_sim_params.get('L1_cable_loss')
+        l5_cable_loss = self.gnss_sim_params.get('L5_cable_loss')
+
+        for i, gnss_pwr_swp in enumerate(self.gnss_pwr_sweep_fine_sweep_ls):
+            self.log.info(f'Start fine GNSS power level sweep part {i + 1}')
+            self.l1_sweep_cnt = self.check_l5_points(gnss_pwr_swp)
+            for gnss_pwr_params in gnss_pwr_swp[1]:
+                json_tag = f'test_'
+                csv_result_tag = ''
+                for ii, pwr in enumerate(
+                        gnss_pwr_params):  # Setup L1 and L5 power
+                    sat_sys = gnss_pwr_swp[0][ii].get('sat').upper()
+                    band = gnss_pwr_swp[0][ii].get('band').upper()
+                    if band == "L1":
+                        pwr_biased = pwr + l1_cable_loss
+                        if pwr != self.current_l1_pwr:
+                            self.position_fix_timeout_cnt = 0
+                            self.current_l1_pwr = pwr
+                    elif band == "L5":
+                        pwr_biased = pwr + l5_cable_loss
+                    else:
+                        pwr_biased = pwr
+                    # Set GNSS Simulator power level
+                    self.gnss_simulator.ping_inst()
+                    self.gnss_simulator.set_scenario_power(
+                        power_level=pwr_biased,
+                        sat_system=sat_sys,
+                        freq_band=band)
+                    self.log.info(f'Set {sat_sys} {band} with power {pwr}')
+                    json_tag = json_tag + f'{sat_sys}_{band}_{pwr}'
+                    csv_result_tag = csv_result_tag + f'{band}_{pwr}_'
+
+                if self.current_l1_pwr < self.l1_sensitivity_point and self.enable_early_terminate == 1:
+                    position_error_mean = -1
+                    position_error_std = -1
+                    hacc_mean = -1
+                    hacc_std = -1
+                else:
+                    position_error_mean, position_error_std, hacc_mean, hacc_std = self.gnss_tracking_L5_position_error_capture(
+                        json_tag)
+                writer = csv.writer(csvfile)
+                writer.writerow([
+                    csv_result_tag, position_error_mean, position_error_std,
+                    hacc_mean, hacc_std
+                ])
+        csvfile.close()
diff --git a/acts_tests/tests/google/gnss/LabTtffGeneralCoexTest.py b/acts_tests/tests/google/gnss/LabTtffGeneralCoexTest.py
index 24da4d3..664c5b6 100644
--- a/acts_tests/tests/google/gnss/LabTtffGeneralCoexTest.py
+++ b/acts_tests/tests/google/gnss/LabTtffGeneralCoexTest.py
@@ -16,7 +16,8 @@
 
 from acts_contrib.test_utils.gnss import LabTtffTestBase as lttb
 from acts_contrib.test_utils.gnss.gnss_test_utils import launch_eecoexer
-from acts_contrib.test_utils.gnss.gnss_test_utils import excute_eecoexer_function
+from acts_contrib.test_utils.gnss.gnss_test_utils import execute_eecoexer_function
+
 
 
 class LabTtffGeneralCoexTest(lttb.LabTtffTestBase):
@@ -26,6 +27,8 @@
         super().setup_class()
         req_params = ['coex_testcase_ls']
         self.unpack_userparams(req_param_names=req_params)
+        self.test_cmd = ''
+        self.stop_cmd = ''
 
     def setup_test(self):
         super().setup_test()
@@ -34,16 +37,9 @@
         self.dut.adb.shell(
             'setprop persist.com.google.eecoexer.cellular.temperature_limit 60')
 
-    def exe_eecoexer_loop_cmd(self, cmd_list=list()):
-        """
-        Function for execute EECoexer command list
-            Args:
-                cmd_list: a list of EECoexer function command.
-                Type, list.
-        """
-        for cmd in cmd_list:
-            self.log.info('Execute EEcoexer Command: {}'.format(cmd))
-            excute_eecoexer_function(self.dut, cmd)
+    def teardown_test(self):
+        super().teardown_test()
+        self.exe_eecoexer_loop_cmd(self.stop_cmd)
 
     def gnss_ttff_ffpe_coex_base(self, mode):
         """
@@ -54,29 +50,35 @@
                 cs(cold start), ws(warm start), hs(hot start)
         """
         # Loop all test case in coex_testcase_ls
-        for test_item in self.coex_testcase_ls:
+        for i, test_item in enumerate(self.coex_testcase_ls):
+
+            if i > 0:
+                self.setup_test()
 
             # get test_log_path from coex_testcase_ls['test_name']
             test_log_path = test_item['test_name']
 
             # get test_cmd from coex_testcase_ls['test_cmd']
-            test_cmd = test_item['test_cmd']
+            self.test_cmd = test_item['test_cmd']
 
             # get stop_cmd from coex_testcase_ls['stop_cmd']
-            stop_cmd = test_item['stop_cmd']
+            self.stop_cmd = test_item['stop_cmd']
 
             # Start aggressor Tx by EEcoexer
-            self.exe_eecoexer_loop_cmd(test_cmd)
+            # self.exe_eecoexer_loop_cmd(test_cmd)
 
             # Start GNSS TTFF FFPE testing
-            self.gnss_ttff_ffpe(mode, test_log_path)
+            self.gnss_ttff_ffpe(mode, test_log_path, self.test_cmd, self.stop_cmd)
 
             # Stop aggressor Tx by EEcoexer
-            self.exe_eecoexer_loop_cmd(stop_cmd)
+            # self.exe_eecoexer_loop_cmd(stop_cmd)
 
             # Clear GTW GPSTool log. Need to clean the log every round of the test.
             self.clear_gps_log()
 
+            if i < len(self.coex_testcase_ls) - 1:
+                self.teardown_test()
+
     def test_gnss_cold_ttff_ffpe_coex(self):
         """
         Cold start TTFF and FFPE GNSS general coex testing
diff --git a/acts_tests/tests/google/gnss/LocationPlatinumTest.py b/acts_tests/tests/google/gnss/LocationPlatinumTest.py
index 6f4c253..a50acb4 100644
--- a/acts_tests/tests/google/gnss/LocationPlatinumTest.py
+++ b/acts_tests/tests/google/gnss/LocationPlatinumTest.py
@@ -67,7 +67,7 @@
         gutils.check_location_service(self.ad)
         if not self.ad.droid.wifiCheckState():
             wutils.wifi_toggle_state(self.ad, True)
-            gutils.connect_to_wifi_network(self.ad, self.wifi_network)
+        gutils.connect_to_wifi_network(self.ad, self.wifi_network)
 
     def get_and_verify_ttff(self, mode):
         """Retrieve ttff with designate mode.
diff --git a/acts_tests/tests/google/native/NativeTest.py b/acts_tests/tests/google/native/NativeTest.py
index b62b456..fb65ae0 100644
--- a/acts_tests/tests/google/native/NativeTest.py
+++ b/acts_tests/tests/google/native/NativeTest.py
@@ -14,22 +14,22 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-import time
 from acts.base_test import BaseTestClass
 from acts_contrib.test_utils.bt.native_bt_test_utils import setup_native_bluetooth
 from acts_contrib.test_utils.bt.bt_test_utils import generate_id_by_size
 
+
 class NativeTest(BaseTestClass):
     tests = None
 
     def __init__(self, controllers):
         BaseTestClass.__init__(self, controllers)
         self.tests = (
-                "test_bool_return_true",
-                "test_bool_return_false",
-                "test_null_return",
-                "test_string_empty_return",
-                "test_max_param_size",
+            "test_bool_return_true",
+            "test_bool_return_false",
+            "test_null_return",
+            "test_string_empty_return",
+            "test_max_param_size",
         )
 
     def setup_class(self):
@@ -55,6 +55,5 @@
         return test_string == self.droid.TestStringMaxReturn(test_string)
 
     def test_specific_param_naming(self):
-        a = [{"string_test":"test", "int_test":1}]
+        a = [{"string_test": "test", "int_test": 1}]
         return self.droid.TestSpecificParamNaming(a)
-
diff --git a/acts_tests/tests/google/net/CoreNetworkingOTATest.py b/acts_tests/tests/google/net/CoreNetworkingOTATest.py
index 019409a..ac5f10a 100755
--- a/acts_tests/tests/google/net/CoreNetworkingOTATest.py
+++ b/acts_tests/tests/google/net/CoreNetworkingOTATest.py
@@ -14,11 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import os
-import re
-import time
-import urllib.request
-
 import acts.base_test
 import acts.signals as signals
 
@@ -49,28 +44,27 @@
         req_params.extend(["download_file", "file_size"])
         self.unpack_userparams(req_params)
 
-        vpn_params = {'vpn_username': self.vpn_username,
-                      'vpn_password': self.vpn_password,
-                      'psk_secret': self.psk_secret,
-                      'client_pkcs_file_name': self.client_pkcs_file_name,
-                      'cert_path_vpnserver': self.cert_path_vpnserver,
-                      'cert_password': self.cert_password}
+        vpn_params = {
+            'vpn_username': self.vpn_username,
+            'vpn_password': self.vpn_password,
+            'psk_secret': self.psk_secret,
+            'client_pkcs_file_name': self.client_pkcs_file_name,
+            'cert_path_vpnserver': self.cert_path_vpnserver,
+            'cert_password': self.cert_password
+        }
 
         # generate legacy vpn profiles
         wutils.wifi_connect(self.dut, self.wifi_network)
         self.xauth_rsa = nutils.generate_legacy_vpn_profile(
-            self.dut, vpn_params,
-            VPN_TYPE.IPSEC_XAUTH_RSA,
+            self.dut, vpn_params, VPN_TYPE.IPSEC_XAUTH_RSA,
             self.vpn_server_addresses[VPN_TYPE.IPSEC_XAUTH_RSA.name][0],
             self.ipsec_server_type[0], self.log_path)
         self.l2tp_rsa = nutils.generate_legacy_vpn_profile(
-            self.dut, vpn_params,
-            VPN_TYPE.L2TP_IPSEC_RSA,
+            self.dut, vpn_params, VPN_TYPE.L2TP_IPSEC_RSA,
             self.vpn_server_addresses[VPN_TYPE.L2TP_IPSEC_RSA.name][0],
             self.ipsec_server_type[0], self.log_path)
         self.hybrid_rsa = nutils.generate_legacy_vpn_profile(
-            self.dut, vpn_params,
-            VPN_TYPE.IPSEC_HYBRID_RSA,
+            self.dut, vpn_params, VPN_TYPE.IPSEC_HYBRID_RSA,
             self.vpn_server_addresses[VPN_TYPE.IPSEC_HYBRID_RSA.name][0],
             self.ipsec_server_type[0], self.log_path)
         self.vpn_profiles = [self.l2tp_rsa, self.hybrid_rsa, self.xauth_rsa]
@@ -81,8 +75,8 @@
 
         # Run OTA below, if ota fails then abort all tests.
         try:
-          for ad in self.android_devices:
-              ota_updater.update(ad)
+            for ad in self.android_devices:
+                ota_updater.update(ad)
         except Exception as err:
             raise signals.TestAbortClass(
                 "Failed up apply OTA update. Aborting tests")
diff --git a/acts_tests/tests/google/net/DataCostTest.py b/acts_tests/tests/google/net/DataCostTest.py
index c8089d9..713a784 100644
--- a/acts_tests/tests/google/net/DataCostTest.py
+++ b/acts_tests/tests/google/net/DataCostTest.py
@@ -13,11 +13,7 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import logging
 import os
-import random
-import socket
-import threading
 import time
 
 from acts import asserts
@@ -40,6 +36,7 @@
 RELIABLE = RELIABILITY | HANDOVER
 TIMEOUT = 6
 
+
 class DataCostTest(base_test.BaseTestClass):
     """ Tests for Wifi Tethering """
 
@@ -62,7 +59,6 @@
             ad.droid.connectivitySetDataWarningLimit(sub_id, -1)
             wutils.reset_wifi(ad)
 
-
     def teardown_test(self):
         if self.tcpdump_pid:
             nutils.stop_tcpdump(self.dut, self.tcpdump_pid, self.test_name)
@@ -70,7 +66,7 @@
 
     def on_fail(self, test_name, begin_time):
         self.dut.take_bug_report(test_name, begin_time)
-        dumpsys_info=self.dut.adb.shell("dumpsys netstats --uid")
+        dumpsys_info = self.dut.adb.shell("dumpsys netstats --uid")
         self.dut.log.info(dumpsys_info)
 
     """ Helper functions """
@@ -82,7 +78,7 @@
             ad: Android device object
         """
         ad.log.info("Clear netstats record.")
-        ad.adb.shell("rm /data/system/netstats/*")
+        ad.adb.shell("if [ $(ls /data/system/netstats/) ]; then rm /data/system/netstats/*; fi")
         asserts.assert_equal("", ad.adb.shell("ls /data/system/netstats/"),
                              "Fail to clear netstats.")
         ad.reboot()
@@ -105,8 +101,7 @@
         if out:
             asserts.assert_true(
                 "HANDOVER|RELIABILITY" in out,
-                "Cell multipath preference should be HANDOVER|RELIABILITY."
-            )
+                "Cell multipath preference should be HANDOVER|RELIABILITY.")
 
     def _get_total_data_usage_for_device(self, ad, conn_type, sub_id):
         """ Get total data usage in MB for device
@@ -125,7 +120,7 @@
         end_time = int(time.time() * 1000) + 2 * 1000 * 60 * 60
         data_usage = ad.droid.connectivityQuerySummaryForDevice(
             conn_type, sub_id, 0, end_time)
-        data_usage /= 1000.0 * 1000.0 # convert data_usage to MB
+        data_usage /= 1000.0 * 1000.0  # convert data_usage to MB
         self.log.info("Total data usage is: %s" % data_usage)
         return data_usage
 
@@ -142,12 +137,8 @@
             asserts.assert_true(val >= exp,
                                 "Multipath value should be at least %s" % exp)
 
-    def _verify_multipath_preferences(self,
-                                      ad,
-                                      wifi_pref,
-                                      cell_pref,
-                                      wifi_network,
-                                      cell_network):
+    def _verify_multipath_preferences(self, ad, wifi_pref, cell_pref,
+                                      wifi_network, cell_network):
         """ Verify mutlipath preferences for wifi and cell networks
 
         Args:
@@ -194,8 +185,8 @@
         self.log.info("wifi network %s" % wifi_network)
 
         # verify mulipath preference values
-        self._verify_multipath_preferences(
-            ad, RELIABLE, RELIABLE, wifi_network, cell_network)
+        self._verify_multipath_preferences(ad, RELIABLE, RELIABLE,
+                                           wifi_network, cell_network)
 
         # set low data limit on mobile data
         total_pre = self._get_total_data_usage_for_device(ad, 0, sub_id)
@@ -210,8 +201,8 @@
         curr_time = time.time()
         while time.time() < curr_time + TIMEOUT:
             try:
-                self._verify_multipath_preferences(
-                    ad, RELIABLE, NONE, wifi_network, cell_network)
+                self._verify_multipath_preferences(ad, RELIABLE, NONE,
+                                                   wifi_network, cell_network)
                 return True
             except signals.TestFailure as e:
                 self.log.debug("%s" % e)
@@ -242,8 +233,8 @@
         self.log.info("wifi network %s" % wifi_network)
 
         # verify multipath preference for wifi and cell networks
-        self._verify_multipath_preferences(
-            ad, RELIABLE, RELIABLE, wifi_network, cell_network)
+        self._verify_multipath_preferences(ad, RELIABLE, RELIABLE,
+                                           wifi_network, cell_network)
 
         # download file with cell network
         ad.droid.connectivityNetworkOpenConnection(cell_network,
@@ -260,8 +251,8 @@
         curr_time = time.time()
         while time.time() < curr_time + TIMEOUT:
             try:
-                self._verify_multipath_preferences(
-                    ad, RELIABLE, NONE, wifi_network, cell_network)
+                self._verify_multipath_preferences(ad, RELIABLE, NONE,
+                                                   wifi_network, cell_network)
                 return True
             except signals.TestFailure as e:
                 self.log.debug("%s" % e)
diff --git a/acts_tests/tests/google/nr/cbr/CellBroadcastInitializationTest.py b/acts_tests/tests/google/nr/cbr/CellBroadcastInitializationTest.py
index ed3ea6a..db7e1a0 100644
--- a/acts_tests/tests/google/nr/cbr/CellBroadcastInitializationTest.py
+++ b/acts_tests/tests/google/nr/cbr/CellBroadcastInitializationTest.py
@@ -18,8 +18,6 @@
 """
 
 import time
-import os
-
 
 from acts.logger import epoch_to_log_line_timestamp
 from acts.keys import Config
@@ -31,19 +29,21 @@
 from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time
 
 
-class CellBroadcastInitializationTest(BaseTestClass):
+class CellBroadcastInitializationTest(TelephonyBaseTest):
     def setup_test(self):
-        super().setup_class()
+        TelephonyBaseTest.setup_class()
         self.number_of_devices = 1
-        self.cbr_init_iteration = self.user_params.get("cbr_init_iteration", 50)
+        self.cbr_init_iteration = self.user_params.get("cbr_init_iteration",
+                                                       50)
 
     def teardown_class(self):
-        super().teardown_class(self)
+        TelephonyBaseTest.teardown_class(self)
 
     def _get_current_time_in_secs(self, ad):
         try:
             c_time = get_device_epoch_time(ad)
-            c_time = epoch_to_log_line_timestamp(c_time).split()[1].split('.')[0]
+            c_time = epoch_to_log_line_timestamp(c_time).split()[1].split(
+                '.')[0]
             return self._convert_formatted_time_to_secs(c_time)
         except Exception as e:
             ad.log.error(e)
@@ -51,7 +51,8 @@
     def _convert_formatted_time_to_secs(self, formatted_time):
         try:
             time_list = formatted_time.split(":")
-            return int(time_list[0]) * 3600 + int(time_list[1]) * 60 + int(time_list[2])
+            return int(time_list[0]) * 3600 + int(time_list[1]) * 60 + int(
+                time_list[2])
         except Exception as e:
             self.log.error(e)
 
@@ -72,13 +73,15 @@
         """
         ad = self.android_devices[0]
 
-        current_cbr_version = ad.get_apk_version('com.google.android.cellbroadcast')
+        current_cbr_version = ad.get_apk_version(
+            'com.google.android.cellbroadcast')
         ad.log.info("Current cbr apk version is %s.", current_cbr_version)
 
         failure_count = 0
         begin_time = self._get_current_time_in_secs(ad)
         for iteration in range(1, self.cbr_init_iteration + 1):
-            msg = "Stress CBR reboot initialization test Iteration: <%s>/<%s>" % (iteration, self.cbr_init_iteration)
+            msg = "Stress CBR reboot initialization test Iteration: <%s>/<%s>" % (
+                iteration, self.cbr_init_iteration)
             self.log.info(msg)
             ad.reboot()
             ad.wait_for_boot_completion()
@@ -93,7 +96,7 @@
         result = True
         if failure_count > 0:
             result = False
-            self.log.error('CBR reboot init stress test: <%s> failures in %s iterations',
-                           failure_count, self.cbr_init_iteration)
+            self.log.error(
+                'CBR reboot init stress test: <%s> failures in %s iterations',
+                failure_count, self.cbr_init_iteration)
         return result
-
diff --git a/acts_tests/tests/google/nr/cbr/CellBroadcastTest.py b/acts_tests/tests/google/nr/cbr/CellBroadcastTest.py
index 4086f2a..cf4786f 100644
--- a/acts_tests/tests/google/nr/cbr/CellBroadcastTest.py
+++ b/acts_tests/tests/google/nr/cbr/CellBroadcastTest.py
@@ -21,14 +21,55 @@
 import time
 import random
 import os
+import re
 
 from acts import signals
 from acts.logger import epoch_to_log_line_timestamp
 from acts.keys import Config
 from acts.test_decorators import test_tracker_info
 from acts.utils import load_config
+from acts.utils import start_standing_subprocess
+from acts.utils import wait_for_standing_subprocess
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts_contrib.test_utils.tel.tel_defines import CARRIER_TEST_CONF_XML_PATH, GERMANY_TELEKOM, QATAR_VODAFONE
+from acts_contrib.test_utils.tel.tel_defines import CARRIER_TEST_CONF_XML_PATH
+from acts_contrib.test_utils.tel.tel_defines import NO_SOUND_TIME
+from acts_contrib.test_utils.tel.tel_defines import NO_VIBRATION_TIME
+from acts_contrib.test_utils.tel.tel_defines import UK_EE
+from acts_contrib.test_utils.tel.tel_defines import COLUMBIA_TELEFONICA
+from acts_contrib.test_utils.tel.tel_defines import JAPAN_EMOBILE
+from acts_contrib.test_utils.tel.tel_defines import JAPAN_WIRELESSCITYPLANNING
+from acts_contrib.test_utils.tel.tel_defines import JAPAN_DOCOMO
+from acts_contrib.test_utils.tel.tel_defines import JAPAN_RAKUTEN
+from acts_contrib.test_utils.tel.tel_defines import KOREA_SKT
+from acts_contrib.test_utils.tel.tel_defines import KOREA_LGU
+from acts_contrib.test_utils.tel.tel_defines import VENEZUELA
+from acts_contrib.test_utils.tel.tel_defines import RUSSIA
+from acts_contrib.test_utils.tel.tel_defines import RUSSIA_MEGAFON
+from acts_contrib.test_utils.tel.tel_defines import TURKEY
+from acts_contrib.test_utils.tel.tel_defines import US
+from acts_contrib.test_utils.tel.tel_defines import US_SPRINT
+from acts_contrib.test_utils.tel.tel_defines import US_USC
+from acts_contrib.test_utils.tel.tel_defines import AZERBAIJAN
+from acts_contrib.test_utils.tel.tel_defines import CHINA
+from acts_contrib.test_utils.tel.tel_defines import SOUTHAFRICA_TELKOM
+from acts_contrib.test_utils.tel.tel_defines import GUATEMALA_TELEFONICA
+from acts_contrib.test_utils.tel.tel_defines import INDIA
+from acts_contrib.test_utils.tel.tel_defines import HUNGARY_TELEKOM
+from acts_contrib.test_utils.tel.tel_defines import CROATIA_HRVATSKI
+from acts_contrib.test_utils.tel.tel_defines import CZECH_TMOBILE
+from acts_contrib.test_utils.tel.tel_defines import SLOVAKIA_TELEKOM
+from acts_contrib.test_utils.tel.tel_defines import AUSTRIA_MAGENTA
+from acts_contrib.test_utils.tel.tel_defines import POLAND_TMOBILE
+from acts_contrib.test_utils.tel.tel_defines import AUSTRIA_TMOBILE
+from acts_contrib.test_utils.tel.tel_defines import MACEDONIA_TELEKOM
+from acts_contrib.test_utils.tel.tel_defines import MONTENEGRO_TELEKOM
+from acts_contrib.test_utils.tel.tel_defines import MEXICO
+from acts_contrib.test_utils.tel.tel_defines import BAHAMAS
+from acts_contrib.test_utils.tel.tel_defines import UKRAINE
+from acts_contrib.test_utils.tel.tel_defines import NORWAY
+from acts_contrib.test_utils.tel.tel_defines import GERMANY_TELEKOM
+from acts_contrib.test_utils.tel.tel_defines import QATAR_VODAFONE
+from acts_contrib.test_utils.tel.tel_defines import CBR_APEX_PACKAGE
 from acts_contrib.test_utils.tel.tel_defines import CLEAR_NOTIFICATION_BAR
 from acts_contrib.test_utils.tel.tel_defines import DEFAULT_ALERT_TYPE
 from acts_contrib.test_utils.tel.tel_defines import EXPAND_NOTIFICATION_BAR
@@ -42,6 +83,7 @@
 from acts_contrib.test_utils.tel.tel_defines import MEXICO_TELEFONICA
 from acts_contrib.test_utils.tel.tel_defines import ELSALVADOR_TELEFONICA
 from acts_contrib.test_utils.tel.tel_defines import PERU_TELEFONICA
+from acts_contrib.test_utils.tel.tel_defines import SPAIN_TELEFONICA
 from acts_contrib.test_utils.tel.tel_defines import PERU_ENTEL
 from acts_contrib.test_utils.tel.tel_defines import KOREA
 from acts_contrib.test_utils.tel.tel_defines import TAIWAN
@@ -101,18 +143,24 @@
 from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time
 from acts_contrib.test_utils.tel.tel_data_utils import wait_for_data_connection
 from acts_contrib.test_utils.tel.tel_wifi_utils import wifi_toggle_state
+from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode
 from acts_contrib.test_utils.tel.tel_wifi_utils import ensure_wifi_connected
-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.net import ui_utils as uutils
 from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
 from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown
 from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_data_for_subscription
 from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_voice_general
-from test_utils.tel.tel_5g_test_utils import provision_device_for_5g
-from test_utils.tel.tel_ims_utils import set_wfc_mode_for_subscription
-from test_utils.tel.tel_ims_utils import wait_for_wfc_enabled
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_on_rat
+from acts_contrib.test_utils.net.ui_utils import get_element_attributes
+from acts_contrib.test_utils.tel.tel_5g_test_utils import provision_device_for_5g
+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_wfc_enabled
 
+VIBRATION_START_TIME = "startTime"
+VIBRATION_END_TIME = "endTime"
+INVALID_VIBRATION_TIME = "0"
+INVALID_SUBSCRIPTION_ID = -1
 
 class CellBroadcastTest(TelephonyBaseTest):
     def setup_class(self):
@@ -146,18 +194,24 @@
         for info in subInfo:
             if info["simSlotIndex"] >= 0:
                 self.slot_sub_id_list[info["subscriptionId"]] = info["simSlotIndex"]
-        if len(subInfo) > 1:
-            self.android_devices[0].log.info("device is operated at DSDS!")
-        else:
-            self.android_devices[0].log.info("device is operated at single SIM!")
-        self.current_sub_id = self.android_devices[0].droid.subscriptionGetDefaultVoiceSubId()
+        self._check_multisim()
+        self.current_sub_id = self.android_devices[0].droid.subscriptionGetDefaultSubId()
+        # Sets default sub id to pSIM to avoid crashing if returning INVALID_SUBSCRIPTION_ID.
+        if self.current_sub_id == INVALID_SUBSCRIPTION_ID:
+            for sub_id in self.slot_sub_id_list.keys():
+                if self.slot_sub_id_list[sub_id] == 0:
+                    psim_sub_id = sub_id
+                    break;
+            self.android_devices[0].droid.subscriptionSetDefaultSubId(psim_sub_id)
+            self.current_sub_id = self.android_devices[0].droid.subscriptionGetDefaultSubId()
 
-        self.android_devices[0].log.info("Active slot: %d, active voice subscription id: %d",
-                                         self.slot_sub_id_list[self.current_sub_id], self.current_sub_id)
+        self.android_devices[0].log.info("Active slot: %d, active subscription id: %d",
+                                         self.slot_sub_id_list[self.current_sub_id],
+                                         self.current_sub_id)
 
         if hasattr(self, "carrier_test_conf"):
             if isinstance(self.carrier_test_conf, list):
-                self.carrier_test_conf = self.carrier_test_conf[self.slot_sub_id_list[self.current_sub_id]]
+                self.carrier_test_conf = self.carrier_test_conf[0]
             if not os.path.isfile(self.carrier_test_conf):
                 self.carrier_test_conf = os.path.join(
                     self.user_params[Config.key_config_path.value],
@@ -169,6 +223,10 @@
         self.emergency_alert_settings_dict = load_config(self.emergency_alert_settings)
         self.emergency_alert_channels_dict = load_config(self.emergency_alert_channels)
         self._verify_cbr_test_apk_install(self.android_devices[0])
+        self.cbr_version = ""
+        self.cbr_upgrade_version = ""
+        self.cbr_rollback_version = ""
+
 
     def setup_test(self):
         TelephonyBaseTest.setup_test(self)
@@ -178,6 +236,13 @@
         TelephonyBaseTest.teardown_class(self)
 
 
+    def _check_multisim(self):
+        if "dsds" in self.android_devices[0].adb.shell("getprop | grep persist.radio.multisim.config"):
+            self.android_devices[0].log.info("device is operated at DSDS!")
+        else:
+            self.android_devices[0].log.info("device is operated at single SIM!")
+
+
     def _verify_cbr_test_apk_install(self, ad):
         if not ad.is_apk_installed(CBR_TEST_APK):
             cbrtestapk = self.user_params.get("cbrtestapk")
@@ -202,6 +267,26 @@
         if self.android_devices[0].adb.getprop("ro.build.version.release") in ("11", "R"):
             self.verify_vibration = False
 
+    def _get_carrier_test_config_name(self):
+        build_version = self.android_devices[0].adb.getprop("ro.build.version.release")
+        self.android_devices[0].log.info("The device's android release build version: %s",
+                                         build_version)
+        slot_index = self.slot_sub_id_list[self.current_sub_id]
+        try:
+            # S build and below only apply to the single sim, use carrier_test_conf.xml.
+            if type(int(build_version)) == int and int(build_version) <= 12:
+                return f'{CARRIER_TEST_CONF_XML_PATH}carrier_test_conf.xml'
+            # T build and above apply to the dual sim, use carrier_test_conf_sim0.xml or
+            # carrier_test_conf_sim1.xml
+            return f'{CARRIER_TEST_CONF_XML_PATH}carrier_test_conf_sim{slot_index}.xml'
+        except ValueError:
+            # S build and below only apply to the single sim, use carrier_test_conf.xml.
+            if build_version <= "S":
+                return f'{CARRIER_TEST_CONF_XML_PATH}carrier_test_conf.xml'
+            # T build and above apply to the dual sim, use carrier_test_conf_sim0.xml or
+            # carrier_test_conf_sim1.xml
+            return f'{CARRIER_TEST_CONF_XML_PATH}carrier_test_conf_sim{slot_index}.xml'
+
     def _get_toggle_value(self, ad, alert_text=None):
         if alert_text == "Alerts":
             node = uutils.wait_and_get_xml_node(ad, timeout=30, matching_node=2, text=alert_text)
@@ -216,10 +301,22 @@
             uutils.wait_and_click(ad, text=alert_text)
 
     def _has_element(self, ad, alert_text=None):
+        # The Saudiarabia has an alert setting, "Alerts", whose name is the same as the title of
+        #  Wireless emergency alerts UI. We have to skip the title node of Wireless emergency
+        #  alerts UI. So set matching_node to 2 if searching "Alerts" setting.
         if alert_text == "Alerts":
-            return uutils.has_element(ad, text=alert_text, matching_node=2)
+            element_exist = uutils.has_element(ad, text=alert_text, matching_node=2)
         else:
-            return uutils.has_element(ad, text=alert_text)
+            element_exist = uutils.has_element(ad, text=alert_text)
+        # Checks if the alert title node also has a sibling node for switch button.
+        if element_exist:
+            if alert_text == "Alerts":
+                node = uutils.wait_and_get_xml_node(ad, timeout=30, matching_node=2, text=alert_text)
+            else:
+                node = uutils.wait_and_get_xml_node(ad, timeout=30, text=alert_text)
+            if not node.parentNode.nextSibling.firstChild:
+                element_exist = False
+        return element_exist
 
     def _open_wea_settings_page(self, ad):
         ad.adb.shell("am start -a %s -n %s/%s" % (MAIN_ACTIVITY, CBR_PACKAGE, CBR_ACTIVITY))
@@ -250,11 +347,15 @@
         tree.write(self.carrier_test_conf)
 
         # push carrier xml to device
-        ad.log.info("push %s to %s" % (self.carrier_test_conf, CARRIER_TEST_CONF_XML_PATH))
-        ad.adb.push("%s %s" % (self.carrier_test_conf, CARRIER_TEST_CONF_XML_PATH))
+        carrier_test_config_xml = self._get_carrier_test_config_name()
+        ad.log.info("push %s to %s" % (self.carrier_test_conf, carrier_test_config_xml))
+        ad.adb.push("%s %s" % (self.carrier_test_conf, carrier_test_config_xml))
 
         # reboot device
         reboot_device(ad)
+        # b/259586331#18 reboot twice to ensure the correct subscription info of the new region
+        # is correctly loaded to CBR module.
+        reboot_device(ad)
         time.sleep(WAIT_TIME_FOR_ALERTS_TO_POPULATE)
 
         # verify adb property
@@ -359,8 +460,12 @@
         out = ad.adb.shell(DUMPSYS_VIBRATION)
         if out:
             try:
-                starttime = out.split()[2].split('.')[0]
-                endtime = out.split()[5].split('.')[0]
+                starttime = self._get_vibration_time(ad, out, time_info=VIBRATION_START_TIME)
+                if starttime == INVALID_VIBRATION_TIME:
+                    return False
+                endtime = self._get_vibration_time(ad, out, time_info=VIBRATION_END_TIME)
+                if endtime == INVALID_VIBRATION_TIME:
+                    return False
                 starttime = self._convert_formatted_time_to_secs(starttime)
                 endtime = self._convert_formatted_time_to_secs(endtime)
                 vibration_time = endtime - starttime
@@ -378,6 +483,14 @@
         return False
 
 
+    def _get_vibration_time(self, ad, out, time_info=VIBRATION_START_TIME):
+        vibration_info_list = out.split(',')
+        for info in vibration_info_list:
+            if time_info in info:
+                return info.strip().split(' ')[2].split('.')[0]
+        ad.log.error(" Not found %s in the vibration info!", time_info)
+        return INVALID_VIBRATION_TIME
+
     def _verify_sound(self, ad, begintime, expectedtime, offset, calling_package=CBR_PACKAGE):
         if not self.verify_sound:
             return True
@@ -457,12 +570,15 @@
         return alert_in_notification
 
 
-    def _verify_send_receive_wea_alerts(self, ad, region=None, call=False, call_direction=DIRECTION_MOBILE_ORIGINATED):
+    def _verify_send_receive_wea_alerts(self, ad, region=None, call=False, call_direction=DIRECTION_MOBILE_ORIGINATED, test_channel=None, screen_off=False):
         result = True
         # Always clear notifications in the status bar before testing to find alert notification easily.
         self._clear_statusbar_notifications(ad)
         for key, value in self.emergency_alert_channels_dict[region].items():
-
+            channel = int(key)
+            if test_channel:
+                if test_channel != channel:
+                    continue
             if call:
                 if not self._setup_voice_call(self.log,
                                               self.android_devices,
@@ -472,7 +588,6 @@
 
             # Configs
             iteration_result = True
-            channel = int(key)
             alert_text = value["title"]
             alert_expected = value["default_value"]
             wait_for_alert = value.get("alert_time", WAIT_TIME_FOR_ALERT_TO_RECEIVE)
@@ -480,6 +595,14 @@
             sound_time = value.get("sound_time", DEFAULT_SOUND_TIME)
             offset = value.get("offset", DEFAULT_OFFSET)
             alert_type = value.get("alert_type", DEFAULT_ALERT_TYPE)
+            if sound_time == NO_SOUND_TIME:
+                ad.log.info("Skip the verification of sound time because no sound time"
+                            + " is defined for the channel!")
+                self.verify_sound = False
+            if vibration_time == NO_VIBRATION_TIME:
+                ad.log.info("Skip the verification of vibration time because no vibration time"
+                            + " is defined for the channel!")
+                self.verify_vibration = False
 
             # Begin Iteration
             begintime = self._get_current_time_in_secs(ad)
@@ -496,7 +619,11 @@
             if call:
                 hangup_call(self.log, ad)
 
+            if screen_off:
+                ad.adb.shell("input keyevent KEYCODE_POWER")
             time.sleep(wait_for_alert)
+            if screen_off:
+                ad.adb.shell("input keyevent KEYCODE_POWER")
 
             # Receive Alert
             if not self._verify_text_present_on_ui(ad, alert_text):
@@ -571,19 +698,119 @@
         return result
 
 
-    def _send_receive_test_flow(self, region):
+    def _settings_upgrade_cbr_test_flow(self,
+                                        region,
+                                        upgrade_cbr_train_build=False,
+                                        rollback_cbr_train_build=False):
+        """Verifies wea alert settings for upgrade and rollback of a new cbr build.
+
+        The method is also able to verify alert settings on the device UI after
+            upgrading a new cbr build and rolling back.
+        The full path of the new cbr build to be upgraded should be specified
+            in the flag of acts config file, cbr_train_build.
+
+        Args:
+            upgrade_cbr_train_build: perform installing cbr build if True.
+            rollback_cbr_train_build: perform cbr package rollback if True.
+        """
+        ad = self.android_devices[0]
+        result = True
+        self._set_device_to_specific_region(ad, region)
+        time.sleep(WAIT_TIME_FOR_UI)
+
+        test_iteration = [True, rollback_cbr_train_build]
+
+        if upgrade_cbr_train_build:
+            if not self._install_verify_upgrade_cbr_train_build(ad):
+                return False;
+
+        for index in range(len(test_iteration)):
+            test_wea_default_settings = test_iteration[index]
+            if test_wea_default_settings:
+                time.sleep(WAIT_TIME_FOR_UI)
+                if not self._verify_wea_default_settings(ad, region):
+                    result = False
+                log_screen_shot(ad, "default_settings_%s" % region)
+                self._close_wea_settings_page(ad)
+                # Here close wea setting UI and then immediately open the UI that sometimes causes
+                # failing to open the wea setting UI. So we just delay 1 sec after closing
+                # the wea setting UI.
+                time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+                if not self._verify_wea_toggle_settings(ad, region):
+                    log_screen_shot(ad, "toggle_settings_%s" % region)
+                    result = False
+                get_screen_shot_log(ad)
+                self._close_wea_settings_page(ad)
+
+                if index == 0 and rollback_cbr_train_build:
+                    if not self._rollback_verify_original_cbr_build(ad):
+                        return False;
+
+        return result
+
+
+    def _send_receive_test_flow(self, region, test_channel=None):
+        """Verifies wea alert channels.
+
+        Args:
+            test_channel: specify a specific alert channel to be tested. Otherwise,
+                          all alert channels will be tested.
+        """
         ad = self.android_devices[0]
         result = True
         self._set_device_to_specific_region(ad, region)
         time.sleep(WAIT_TIME_FOR_UI)
         ad.log.info("disable DND: %s", CMD_DND_OFF)
         ad.adb.shell(CMD_DND_OFF)
-        if not self._verify_send_receive_wea_alerts(ad, region):
+        if not self._verify_send_receive_wea_alerts(ad, region, test_channel=test_channel):
             result = False
         get_screen_shot_log(ad)
         return result
 
 
+    def _send_receive_upgrade_cbr_test_flow(self, region,
+                                            test_channel=None,
+                                            upgrade_cbr_train_build=False,
+                                            rollback_cbr_train_build=False):
+        """Verifies wea alert channels for upgrade and rollback of a new cbr build.
+
+        The method is also able to verify alert channels on the device after upgrading
+            a new cbr build and rolling back.
+        The full path of the new cbr build to be upgraded should be specified in the flag
+            of acts config file, cbr_train_build.
+        Args:
+            test_channel: specify a specific alert channel to be tested. Otherwise,
+                            all alert channels will be tested.
+            upgrade_cbr_train_build: perform installing cbr build if True.
+            rollback_cbr_train_build: perform cbr package rollback if True.
+        """
+        ad = self.android_devices[0]
+        result = True
+        self._set_device_to_specific_region(ad, region)
+
+        test_iteration = [True, rollback_cbr_train_build]
+
+        if upgrade_cbr_train_build:
+            if not self._install_verify_upgrade_cbr_train_build(ad):
+                return False;
+
+        for index in range(len(test_iteration)):
+            test_wea_default_settings = test_iteration[index]
+            if test_wea_default_settings:
+                time.sleep(WAIT_TIME_FOR_UI)
+                ad.log.info("disable DND: %s", CMD_DND_OFF)
+                ad.adb.shell(CMD_DND_OFF)
+                if not self._verify_send_receive_wea_alerts(ad,
+                                                            region,
+                                                            test_channel=test_channel):
+                    result = False
+                get_screen_shot_log(ad)
+                if index == 0 and rollback_cbr_train_build:
+                    if not self._rollback_verify_original_cbr_build(ad):
+                        return False;
+        return result
+
+
     def _setup_receive_test_flow_wifi(self, region, gen, data):
         """ Setup send/receive WEA with wifi enabled and various RAT."""
         ad = self.android_devices[0]
@@ -640,6 +867,110 @@
             ad.log.error("WFC is not enabled")
             return False
         return True
+
+    def _execute_command_line(self, ad, command):
+        """ Executes command line.
+
+        Returns:
+            [True, stdout] if successful. Otherwise, [False, stderr].
+        """
+        ad.log.info("Execute %s", command)
+        install_proc = start_standing_subprocess(command)
+        wait_for_standing_subprocess(install_proc)
+        out, err = install_proc.communicate()
+        if err:
+            ad.log.error("stderr: %s", err.decode('utf-8'))
+            return [False, err.decode('utf-8')]
+        ad.log.info("stdout: %s",out.decode('utf-8'))
+        return [True, out.decode('utf-8')]
+
+
+    def _get_current_cbr_build_version_code(self, ad):
+        """ Gets the version code of CBR package.
+
+        Returns:
+            Version code if the rollback is successful. Otherwise, None.
+        """
+        cbr_version_code_command = f'adb shell pm list packages --apex-only' + \
+                                   f' --show-versioncode | grep {CBR_APEX_PACKAGE}'
+        result, out = self._execute_command_line(ad, cbr_version_code_command)
+        if not result:
+            return None
+        version_code_regex = f'^package:{CBR_APEX_PACKAGE}\sversionCode:(\d+)$'
+        version_code = re.findall(version_code_regex, out)
+        if not version_code:
+            ad.log.error("Fail to filter version code, check the match pattern: %s!",
+                         version_code_regex)
+            return None
+        ad.log.info("The version of %s in the device is %s.", CBR_APEX_PACKAGE, version_code)
+
+        return version_code
+
+
+
+    def _install_cbr_train_build(self, ad):
+        """ Installs a rollback-enabled CBR train build.
+
+        Returns:
+            True if the rollback is successful. Otherwise, False.
+        """
+
+        cbr_train_build = self.user_params.get("cbr_train_build", "")
+        if not cbr_train_build:
+            ad.log.error("Not define 'cbr_train_build' flag in the config file.")
+            return False
+        ad.log.info("Install %s.", cbr_train_build)
+        cbr_install_command = f'adb install-multi-package  --staged' \
+                              f' --enable-rollback {cbr_train_build}'
+        result, out = self._execute_command_line(ad, cbr_install_command)
+
+        return result
+
+    def _install_verify_upgrade_cbr_train_build(self, ad):
+        """Installs and verifies upgraded cbr train build."""
+
+        self.cbr_version = self._get_current_cbr_build_version_code(ad)
+        if self.cbr_version is None:
+            ad.log.error("Unexpectedly Fail to get cbr version.")
+            return False;
+        if not self._install_cbr_train_build(ad):
+            return False
+        reboot_device(ad)
+        self.cbr_upgrade_version = self._get_current_cbr_build_version_code(ad)
+        if self.cbr_upgrade_version is None:
+            ad.log.error("Unexpectedly Fail to get cbr version.")
+            return False;
+        ad.log.info("The new installed cbr version is %s", self.cbr_upgrade_version)
+        if self.cbr_version[0] == self.cbr_upgrade_version[0]:
+            ad.log.error("The upgrade version shouldn't be the same as the original version,"
+                         " roll back the cbr build!")
+            return False;
+        return True;
+
+    def _rollback_cbr_train_build(self, ad):
+        """ Rolls back to the previous CBR build.
+
+        Returns:
+            True if the rollback is successful. Otherwise, False.
+         """
+        ad.log.info("Roll back CBR module...")
+        cbr_rollback_command = f'adb shell pm rollback-app {CBR_APEX_PACKAGE}'
+        result, out = self._execute_command_line(ad, cbr_rollback_command)
+
+        return result
+
+    def _rollback_verify_original_cbr_build(self, ad):
+        """ Checks if the cbr build is rolled back the factory build. """
+        if not self._rollback_cbr_train_build(ad):
+            return False
+        reboot_device(ad)
+        self.cbr_rollback_version = self._get_current_cbr_build_version_code(ad)
+        ad.log.info("The rollback cbr version is %s", self.cbr_rollback_version)
+        if self.cbr_version[0] != self.cbr_rollback_version[0]:
+            ad.log.error("The rollback version should be the same as the original version!")
+            return False;
+        return True
+
     """ Tests Begin """
 
 
@@ -793,6 +1124,21 @@
         return self._settings_test_flow(PERU_TELEFONICA)
 
 
+    @test_tracker_info(uuid="087da90b-d847-4bf7-8504-4006bb1d6816")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_spain_telefonica(self):
+        """ Verifies Wireless Emergency Alert settings for Spain_Telefonica
+
+        configures the device to Spain_Telefonica
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(SPAIN_TELEFONICA)
+
+
     @test_tracker_info(uuid="cc0e0f64-2c77-4e20-b55e-6f555f7ecb97")
     @TelephonyBaseTest.tel_test_wrap
     def test_default_alert_settings_elsalvador_telefonica(self):
@@ -1213,6 +1559,466 @@
         return self._settings_test_flow(QATAR_VODAFONE)
 
 
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_mexico(self):
+        """ Verifies Wireless Emergency Alert settings for Mexico
+
+        configures the device to Mexico
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(MEXICO)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_bahamas(self):
+        """ Verifies Wireless Emergency Alert settings for Bahamas
+
+        configures the device to Bahamas
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(BAHAMAS)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_uk_ee(self):
+        """ Verifies Wireless Emergency Alert settings for UK_EE
+
+        configures the device to UK EE
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(UK_EE)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_columbia_telefonica(self):
+        """ Verifies Wireless Emergency Alert settings for COLUMBIA_TELEFONICA
+
+        configures the device to COLUMBIA TELEFONICA
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(COLUMBIA_TELEFONICA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_japan_emobile(self):
+        """ Verifies Wireless Emergency Alert settings for JAPAN_EMOBILE
+
+        configures the device to JAPAN EMOBILE
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(JAPAN_EMOBILE)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_japan_wirelesscityplanning(self):
+        """ Verifies Wireless Emergency Alert settings for JAPAN_WIRELESSCITYPLANNING
+
+        configures the device to JAPAN WIRELESS CITY PLANNING
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(JAPAN_WIRELESSCITYPLANNING)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_japan_docomo(self):
+        """ Verifies Wireless Emergency Alert settings for JAPAN_DOCOMO
+
+        configures the device to JAPAN DOCOMO
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(JAPAN_DOCOMO)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_japan_rakuten(self):
+        """ Verifies Wireless Emergency Alert settings for JAPAN_RAKUTEN
+
+        configures the device to JAPAN RAKUTEN
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(JAPAN_RAKUTEN)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_korea_skt(self):
+        """ Verifies Wireless Emergency Alert settings for KOREA_SKT
+
+        configures the device to KOREA SKT
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(KOREA_SKT)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_korea_lgu(self):
+        """ Verifies Wireless Emergency Alert settings for KOREA_LGU
+
+        configures the device to KOREA LGU
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(KOREA_LGU)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_venezuela(self):
+        """ Verifies Wireless Emergency Alert settings for VENEZUELA
+
+        configures the device to VENEZUELA
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(VENEZUELA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_russia(self):
+        """ Verifies Wireless Emergency Alert settings for RUSSIA
+
+        configures the device to RUSSIA
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(RUSSIA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_russia_megafon(self):
+        """ Verifies Wireless Emergency Alert settings for RUSSIA_MEGAFON
+
+        configures the device to RUSSIA MEGAFON
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(RUSSIA_MEGAFON)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_turkey(self):
+        """ Verifies Wireless Emergency Alert settings for TURKEY
+
+        configures the device to TURKEY
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(TURKEY)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_us(self):
+        """ Verifies Wireless Emergency Alert settings for US
+
+        configures the device to US
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(US)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_us_sprint(self):
+        """ Verifies Wireless Emergency Alert settings for US_SPRINT
+
+        configures the device to US SPRINT
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(US_SPRINT)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_us_usc(self):
+        """ Verifies Wireless Emergency Alert settings for US_USC
+
+        configures the device to US USC
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(US_USC)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_azerbaijan(self):
+        """ Verifies Wireless Emergency Alert settings for AZERBAIJAN
+
+        configures the device to AZERBAIJAN
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(AZERBAIJAN)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_china(self):
+        """ Verifies Wireless Emergency Alert settings for CHINA
+
+        configures the device to CHINA
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(CHINA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_southafrica_telkom(self):
+        """ Verifies Wireless Emergency Alert settings for SOUTHAFRICA_TELKOM
+
+        configures the device to SOUTH AFRICA TELKOM
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(SOUTHAFRICA_TELKOM)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_guatemala_telefonica(self):
+        """ Verifies Wireless Emergency Alert settings for GUATEMALA_TELEFONICA
+
+        configures the device to GUATEMALA TELEFONICA
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(GUATEMALA_TELEFONICA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_india(self):
+        """ Verifies Wireless Emergency Alert settings for INDIA
+
+        configures the device to INDIA
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(INDIA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_hungary_telekom(self):
+        """ Verifies Wireless Emergency Alert settings for HUNGARY_TELEKOM
+
+        configures the device to HUNGARY TELEKOM
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(HUNGARY_TELEKOM)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_croatia_hrvatski(self):
+        """ Verifies Wireless Emergency Alert settings for CROATIA_HRVATSKI
+
+        configures the device to CROATIA HRVATSKI
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(CROATIA_HRVATSKI)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_czech_tmobile(self):
+        """ Verifies Wireless Emergency Alert settings for CZECH_TMOBILE
+
+        configures the device to CZECH TMOBILE
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(CZECH_TMOBILE)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_slovakia_telekom(self):
+        """ Verifies Wireless Emergency Alert settings for SLOVAKIA_TELEKOM
+
+        configures the device to SLOVAKIA TELEKOM
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(SLOVAKIA_TELEKOM)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_austria_magenta(self):
+        """ Verifies Wireless Emergency Alert settings for AUSTRIA_MAGENTA
+
+        configures the device to AUSTRIA MAGENTA
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(AUSTRIA_MAGENTA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_poland_tmobile(self):
+        """ Verifies Wireless Emergency Alert settings for POLAND_TMOBILE
+
+        configures the device to POLAND TMOBILE
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(POLAND_TMOBILE)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_austria_tmobile(self):
+        """ Verifies Wireless Emergency Alert settings for AUSTRIA_TMOBILE
+
+        configures the device to AUSTRIA TMOBILE
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(AUSTRIA_TMOBILE)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_macedonia_telekom(self):
+        """ Verifies Wireless Emergency Alert settings for MACEDONIA_TELEKOM
+
+        configures the device to MACEDONIA TELEKOM
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(MACEDONIA_TELEKOM)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_montenegro_telekom(self):
+        """ Verifies Wireless Emergency Alert settings for MONTENEGRO_TELEKOM
+
+        configures the device to MONTENEGRO TELEKOM
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(MONTENEGRO_TELEKOM)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_ukraine(self):
+        """ Verifies Wireless Emergency Alert settings for UKRAINE
+
+        configures the device to UKRAINE
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(UKRAINE)
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_norway(self):
+        """ Verifies Wireless Emergency Alert settings for NORWAY
+
+        configures the device to NORWAY
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(NORWAY)
+
     @test_tracker_info(uuid="f3a99475-a23f-427c-a371-d2a46d357d75")
     @TelephonyBaseTest.tel_test_wrap
     def test_send_receive_alerts_australia(self):
@@ -1655,6 +2461,23 @@
         return self._send_receive_test_flow(PERU_TELEFONICA)
 
 
+    @test_tracker_info(uuid="73a4cefc-42e1-4e68-9680-1ac135f424a4")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_spain_telefonica(self):
+        """ Verifies Wireless Emergency Alerts for SPAIN_TELEFONICA
+
+        configures the device to SPAIN_TELEFONICA
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(SPAIN_TELEFONICA)
+
+
     @test_tracker_info(uuid="fefb293a-5c22-45b2-9323-ccb355245c9a")
     @TelephonyBaseTest.tel_test_wrap
     def test_send_receive_alerts_puertorico(self):
@@ -1860,6 +2683,518 @@
 
 
     @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_mexico(self):
+        """ Verifies Wireless Emergency Alerts for Mexico.
+
+        configures the device to Mexico
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(MEXICO)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_bahamas(self):
+        """ Verifies Wireless Emergency Alerts for Bahamas.
+
+        configures the device to Bahamas
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(BAHAMAS)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_uk_ee(self):
+        """ Verifies Wireless Emergency Alerts for UK_EE.
+
+        configures the device to UK EE
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(UK_EE)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_columbia_telefonica(self):
+        """ Verifies Wireless Emergency Alerts for COLUMBIA_TELEFONICA.
+
+        configures the device to COLUMBIA TELEFONICA
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(COLUMBIA_TELEFONICA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_japan_emobile(self):
+        """ Verifies Wireless Emergency Alerts for JAPAN_EMOBILE.
+
+        configures the device to JAPAN EMOBILE
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(JAPAN_EMOBILE)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_japan_wirelesscityplanning(self):
+        """ Verifies Wireless Emergency Alerts for JAPAN_WIRELESSCITYPLANNING.
+
+        configures the device to JAPAN WIRELESS CITY PLANNING
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(JAPAN_WIRELESSCITYPLANNING)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_japan_docomo(self):
+        """ Verifies Wireless Emergency Alerts for JAPAN_DOCOMO.
+
+        configures the device to JAPAN DOCOMO
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(JAPAN_DOCOMO)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_japan_rakuten(self):
+        """ Verifies Wireless Emergency Alerts for JAPAN_RAKUTEN.
+
+        configures the device to JAPAN RAKUTEN
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(JAPAN_RAKUTEN)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_korea_skt(self):
+        """ Verifies Wireless Emergency Alerts for KOREA_SKT.
+
+        configures the device to KOREA SKT
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(KOREA_SKT)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_korea_lgu(self):
+        """ Verifies Wireless Emergency Alerts for KOREA_LGU.
+
+        configures the device to KOREA LGU
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(KOREA_LGU)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_venezuela(self):
+        """ Verifies Wireless Emergency Alerts for VENEZUELA.
+
+        configures the device to VENEZUELA
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(VENEZUELA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_russia(self):
+        """ Verifies Wireless Emergency Alerts for RUSSIA.
+
+        configures the device to RUSSIA
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(RUSSIA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_russia_megafon(self):
+        """ Verifies Wireless Emergency Alerts for RUSSIA_MEGAFON.
+
+        configures the device to RUSSIA MEGAFON
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(RUSSIA_MEGAFON)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_turkey(self):
+        """ Verifies Wireless Emergency Alerts for TURKEY.
+
+        configures the device to TURKEY
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(TURKEY)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_us(self):
+        """ Verifies Wireless Emergency Alerts for US.
+
+        configures the device to US
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(US)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_us_sprint(self):
+        """ Verifies Wireless Emergency Alerts for US_SPRINT.
+
+        configures the device to US SPRINT
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(US_SPRINT)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_us_usc(self):
+        """ Verifies Wireless Emergency Alerts for US_USC.
+
+        configures the device to US USC
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(US_USC)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_azerbaijan(self):
+        """ Verifies Wireless Emergency Alerts for AZERBAIJAN.
+
+        configures the device to AZERBAIJAN
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(AZERBAIJAN)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_china(self):
+        """ Verifies Wireless Emergency Alerts for CHINA.
+
+        configures the device to CHINA
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(CHINA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_southafrica_telkom(self):
+        """ Verifies Wireless Emergency Alerts for SOUTHAFRICA_TELKOM.
+
+        configures the device to SOUTHAFRICA TELKOM
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(SOUTHAFRICA_TELKOM)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_guatemala_telefonica(self):
+        """ Verifies Wireless Emergency Alerts for GUATEMALA_TELEFONICA.
+
+        configures the device to GUATEMALA TELEFONICA
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(GUATEMALA_TELEFONICA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_india(self):
+        """ Verifies Wireless Emergency Alerts for INDIA.
+
+        configures the device to INDIA
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(INDIA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_hungary_telekom(self):
+        """ Verifies Wireless Emergency Alerts for HUNGARY_TELEKOM.
+
+        configures the device to HUNGARY_TELEKOM
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(HUNGARY_TELEKOM)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_croatia_hrvatski(self):
+        """ Verifies Wireless Emergency Alerts for CROATIA_HRVATSKI.
+
+        configures the device to CROATIA HRVATSKI
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(CROATIA_HRVATSKI)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_czech_tmobile(self):
+        """ Verifies Wireless Emergency Alerts for CZECH_TMOBILE.
+
+        configures the device to CZECH TMOBILE
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(CZECH_TMOBILE)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_slovakia_telekom(self):
+        """ Verifies Wireless Emergency Alerts for SLOVAKIA_TELEKOM.
+
+        configures the device to SLOVAKIA TELEKOM
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(SLOVAKIA_TELEKOM)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_austria_magenta(self):
+        """ Verifies Wireless Emergency Alerts for AUSTRIA_MAGENTA.
+
+        configures the device to AUSTRIA MAGENTA
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(AUSTRIA_MAGENTA)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_poland_tmobile(self):
+        """ Verifies Wireless Emergency Alerts for POLAND_TMOBILE.
+
+        configures the device to POLAND TMOBILE
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(POLAND_TMOBILE)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_macedonia_telekom(self):
+        """ Verifies Wireless Emergency Alerts for MACEDONIA_TELEKOM.
+
+        configures the device to MACEDONIA TELEKOM
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(MACEDONIA_TELEKOM)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_montenegro_telekom(self):
+        """ Verifies Wireless Emergency Alerts for MONTENEGRO_TELEKOM.
+
+        configures the device to MONTENEGRO_TELEKOM
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(MONTENEGRO_TELEKOM)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_ukraine(self):
+        """ Verifies Wireless Emergency Alerts for UKRAINE.
+
+        configures the device to UKRAINE
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(UKRAINE)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_norway(self):
+        """ Verifies Wireless Emergency Alerts for NORWAY.
+
+        configures the device to NORWAY
+        send alerts across all channels,
+        verify if alert is received correctly
+        verify sound and vibration timing
+        click on OK/exit alert and verify text
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._send_receive_test_flow(NORWAY)
+
+
+    @TelephonyBaseTest.tel_test_wrap
     def test_send_receive_alerts_5g_wifi_us_vzw(self):
         """ Verifies WEA with WiFi and 5G NSA data network enabled for US Verizon.
 
@@ -2134,7 +3469,7 @@
         """ Verifies WEA during VoWiFi call for US Verizon.
 
         configures the device to US Verizon
-        enables WFC mode and disable 5G NSA data network.
+        enables WFC mode and disable 4G data network.
         connects to internet via WiFi.
         sends alerts across all channels and initiates mo VoWiFi call respectively.
         verify if alert is received correctly
@@ -2164,7 +3499,7 @@
         """ Verifies WEA during VoWiFi call for US Verizon.
 
         configures the device to US Verizon
-        enables WFC mode and disable 5G NSA data network.
+        enables WFC mode and disable 3G data network.
         connects to internet via WiFi.
         sends alerts across all channels and initiates mo VoWiFi call respectively.
         verify if alert is received correctly
@@ -2188,3 +3523,1379 @@
         get_screen_shot_log(self.android_devices[0])
         return result
 
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_toggle_apm(self):
+        """Verify WEA at APM on and off.
+
+        Set device’s region to US Verizon
+        Turn off APM mode
+        Verify emergency alerts
+        Turn on APM mode
+        Verify emergency alerts
+        Turn off APM mode
+        Verify emergency alerts
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        ad = self.android_devices[0]
+        result = True
+        self._set_device_to_specific_region(ad, US_VZW)
+        time.sleep(WAIT_TIME_FOR_UI)
+        ad.log.info("disable DND: %s", CMD_DND_OFF)
+        ad.adb.shell(CMD_DND_OFF)
+        ad.log.info("set device to US Verizon and APM off!")
+        toggle_airplane_mode(ad.log, ad, False)
+        time.sleep(WAIT_TIME_FOR_UI)
+        if not self._verify_send_receive_wea_alerts(ad, US_VZW, test_channel=4370):
+            result = False
+
+        ad.log.info("set APM on and verify WEA setting!")
+        toggle_airplane_mode(ad.log, ad, True)
+        time.sleep(WAIT_TIME_FOR_UI)
+        if not self._verify_send_receive_wea_alerts(ad, US_VZW, test_channel=4370):
+            result = False
+
+        ad.log.info("set APM off and verify WEA setting!")
+        toggle_airplane_mode(ad.log, ad, False)
+        time.sleep(WAIT_TIME_FOR_UI)
+        if not self._verify_send_receive_wea_alerts(ad, US_VZW, test_channel=4370):
+            result = False
+        get_screen_shot_log(ad)
+        return result
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_handover_lte_3g(self):
+        """Verify WEA during handover btw lte and 3g.
+
+        Set device to US Verizon
+        Set network preferred mode to RAT LTE
+        Verify emergency alerts
+        Set network preferred mode to RAT 3G
+        Verify emergency alerts
+        Set network preferred mode to RAT LTE
+        Verify emergency alerts
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        ad = self.android_devices[0]
+        result = True
+        self._set_device_to_specific_region(ad, US_VZW)
+        time.sleep(WAIT_TIME_FOR_UI)
+        ad.log.info("disable DND: %s", CMD_DND_OFF)
+        ad.adb.shell(CMD_DND_OFF)
+        time.sleep(WAIT_TIME_FOR_UI)
+
+        ad.log.info("Set device on volte!")
+        if not phone_setup_on_rat(ad.log, ad, 'volte'):
+            return False
+        if not self._verify_send_receive_wea_alerts(ad, US_VZW, test_channel=4370):
+            result = False
+
+        ad.log.info("Set device on 3g!")
+        if not phone_setup_on_rat(ad.log, ad, '3g'):
+            return False
+        if not self._verify_send_receive_wea_alerts(ad, US_VZW, test_channel=4370):
+            result = False
+
+        ad.log.info("Set device on volte!")
+        if not phone_setup_on_rat(ad.log, ad, 'volte'):
+            return False
+        if not self._verify_send_receive_wea_alerts(ad, US_VZW, test_channel=4370):
+            result = False
+        get_screen_shot_log(ad)
+
+        return result
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_handover_5g_lte(self):
+        """Verify WEA during handover btw 5g and lte.
+
+        Set device to US Verizon
+        Set network preferred mode to RAT 5G NSA
+        Verify emergency alerts
+        Set network preferred mode to RAT LTE
+        Verify emergency alerts
+        Set network preferred mode to RAT 5G NSA
+        Verify emergency alerts
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        ad = self.android_devices[0]
+        result = True
+        self._set_device_to_specific_region(ad, US_VZW)
+        time.sleep(WAIT_TIME_FOR_UI)
+        ad.log.info("disable DND: %s", CMD_DND_OFF)
+        ad.adb.shell(CMD_DND_OFF)
+        time.sleep(WAIT_TIME_FOR_UI)
+
+        ad.log.info("Set device on 5g nsa!")
+        if not phone_setup_on_rat(ad.log, ad, '5g'):
+            return False
+        if not self._verify_send_receive_wea_alerts(ad, US_VZW, test_channel=4370):
+            result = False
+
+        ad.log.info("Set device on volte!")
+        if not phone_setup_on_rat(ad.log, ad, 'volte'):
+            return False
+        if not self._verify_send_receive_wea_alerts(ad, US_VZW, test_channel=4370):
+            result = False
+
+        ad.log.info("Set device on 5g nsa!")
+        if not phone_setup_on_rat(ad.log, ad, '5g'):
+            return False
+        if not self._verify_send_receive_wea_alerts(ad, US_VZW, test_channel=4370):
+            result = False
+        get_screen_shot_log(ad)
+        return result
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_send_receive_alerts_sideload_cbr_module(self):
+        """Verify WEA after sideloading a patch
+
+        Get value of cbr_patch parameter defined in acts config file.
+            If cbr_patch is not defined or empty, skip the test.
+        Install cbr patch.
+        Reboot device.
+        Set device to US Verizon.
+        Verify emergency alerts.
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        result = True
+        ad = self.android_devices[0]
+        cbr_patch_fetch = self.user_params.get("cbr_patch", "")
+        if cbr_patch_fetch:
+            ad.log.info("Download %s and install it.", cbr_patch_fetch)
+            cbr_patch_fetch = cbr_patch_fetch + " " + self.log_path
+            ad.log.info("%s", cbr_patch_fetch)
+            self.fetch_proc = start_standing_subprocess(cbr_patch_fetch)
+            wait_for_standing_subprocess(self.fetch_proc)
+            out, err = self.fetch_proc.communicate()
+            if err:
+                ad.log.info("%s", err.decode('utf-8'))
+                return False
+        else:
+            raise signals.TestSkip("No available cbr patch. Skip test!");
+
+        ad.log.info("Successfully install it.")
+        ad.adb.install("-r %s" % self.log_path+"/com.google.android.cellbroadcast.apex")
+        reboot_device(ad)
+        self._set_device_to_specific_region(ad, US_VZW)
+        time.sleep(WAIT_TIME_FOR_UI)
+        ad.log.info("disable DND: %s", CMD_DND_OFF)
+        ad.adb.shell(CMD_DND_OFF)
+        time.sleep(WAIT_TIME_FOR_UI)
+        if not self._verify_send_receive_wea_alerts(ad, US_VZW, test_channel=4370):
+            result = False
+        get_screen_shot_log(ad)
+        return result
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_alert_not_dismiss_after_clicking_home_button(self):
+        """Verify if alert dismisses when clicking home button.
+
+        1. Set device region to Chile
+        2. Send CBR 4370 alert
+        3. Hide alert in notification drawer by clicking home button
+        4. Verify if alert is in notification drawer
+        5. Show and verify alert dialog is 4370 alert
+        6. dismiss alert
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        ad = self.android_devices[0]
+        self._clear_statusbar_notifications(ad)
+        self._set_device_to_specific_region(ad, CHILE_TELEFONICA)
+        time.sleep(WAIT_TIME_FOR_UI)
+        ad.log.info("disable DND: %s", CMD_DND_OFF)
+        ad.adb.shell(CMD_DND_OFF)
+
+        alert_text = self.emergency_alert_channels_dict[CHILE_TELEFONICA]["4370"]["title"]
+        sequence_num = random.randrange(10000, 40000)
+        ad.log.info("%s for %s: %s", alert_text, CHILE_TELEFONICA, 4370)
+        # Send Alert
+        ad.droid.cbrSendTestAlert(sequence_num, 4370)
+
+        time.sleep(WAIT_TIME_FOR_UI)
+        ad.log.info("Hide the alert channel %s in the notification drawer by clicking home button!", 4370)
+        ad.adb.shell("input keyevent KEYCODE_HOME")
+        time.sleep(WAIT_TIME_FOR_UI)
+        alert_in_notification = False
+        if self._popup_alert_in_statusbar_notifications(ad, alert_text):
+            ad.log.info("Found the alert channel %d in the notification drawer!", 4370)
+            # Verify alert text in message.
+            alert_in_notification = self._verify_text_present_on_ui(ad, alert_text)
+            if not alert_in_notification:
+                ad.log.error("The alert title is not expected, %s", alert_text)
+            self._exit_alert_pop_up(ad)
+        else:
+            ad.log.error(" Couldn't find the alert channel %d in the notification drawer!", 4370)
+
+        return alert_in_notification
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_alert_unread_count(self):
+        """ Verify unread alert count.
+
+        1. Set device region to Korea
+        2. Open alert setting UI and turn off show full screen messages
+        3. Send and hide 4370, 4371 and 4372 alerts for three times each in sequence
+        4. Show alert dialog from notification drawer
+        5. Verify if alert count is 9
+        6. Verify if each alert is correct
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        ad = self.android_devices[0]
+        self._clear_statusbar_notifications(ad)
+
+        self._set_device_to_specific_region(ad, KOREA)
+        time.sleep(WAIT_TIME_FOR_UI)
+        ad.log.info("disable DND: %s", CMD_DND_OFF)
+        ad.adb.shell(CMD_DND_OFF)
+
+        self._open_wea_settings_page(ad)
+        full_screen_setting = "Show full-screen messages"
+        if not self._has_element(ad, full_screen_setting):
+            for _ in range(3):
+                ad.adb.shell(SCROLL_DOWN)
+            if not self._has_element(ad, full_screen_setting):
+                ad.log.error("UI - %s missing", full_screen_setting)
+                return False
+
+        full_screen_setting_value = self._get_toggle_value(ad, full_screen_setting)
+        if full_screen_setting_value == "true":
+            # Turn off show full-screen messages
+            self._wait_and_click(ad, full_screen_setting)
+            time.sleep(WAIT_TIME_FOR_UI)
+        self._close_wea_settings_page(ad)
+        time.sleep(WAIT_TIME_FOR_UI)
+
+        test_alert_channels = [4370, 4371, 4372]
+        for channel in test_alert_channels:
+        # Send and hide alert
+            for iterate in range(1, 4):
+                alert_text = self.emergency_alert_channels_dict[KOREA][str(channel)]["title"]
+                sequence_num = random.randrange(10000, 40000)
+                ad.log.info("%s for %s: %s", alert_text, KOREA, channel)
+                ad.droid.cbrSendTestAlert(sequence_num, channel)
+                time.sleep(WAIT_TIME_FOR_UI)
+                ad.adb.shell("input keyevent KEYCODE_HOME")
+
+        if self._popup_alert_in_statusbar_notifications(ad, "New alerts"):
+            ad.log.info("Found alerts in the notification drawer!")
+        else:
+            ad.log.error(" Couldn't find the alert in the notification drawer!")
+            return False
+
+        ok_button_attrs = get_element_attributes(ad, text_contains="OK")
+        result = True
+        if not "1/9" in ok_button_attrs["text"].value:
+            result = False
+            ad.log.error("Unread alert count is incorrect, %s", ok_button_attrs["text"])
+        else:
+            ad.log.info("Unread alert count is 9!")
+        test_alert_channels.reverse()
+        for channel in test_alert_channels:
+            for iterate in range(1, 4):
+                alert_text = self.emergency_alert_channels_dict[KOREA][str(channel)]["title"]
+                ad.log.info("Try to dismiss %s alert", alert_text)
+                if not self._has_element(ad, alert_text):
+                    result = False
+                    ad.log.error("Alert is incorrect, should be %s", alert_text)
+                self._exit_alert_pop_up(ad)
+
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_alert_datetime_chile_telefonica(self):
+        """ Verifies the datetime format of alert messages for Chile.
+
+        Set the device's region to Chile
+        Send channel 4370 alert
+        Get the alert time string on the alert dialog
+        Verify if the format of the alert time is DD/MM/YYYY hh:mm PM|AM
+        Get device time
+        Verify if the alert time is same as device time
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        result = True
+        ad = self.android_devices[0]
+        self._set_device_to_specific_region(ad, CHILE_TELEFONICA)
+        time.sleep(WAIT_TIME_FOR_UI)
+        ad.log.info("disable DND: %s", CMD_DND_OFF)
+        ad.adb.shell(CMD_DND_OFF)
+        alert_text = self.emergency_alert_channels_dict[CHILE_TELEFONICA][str(4370)]["title"]
+        sequence_num = random.randrange(10000, 40000)
+        ad.log.info("%s for %s: %s", alert_text, CHILE_TELEFONICA, 4370)
+        ad.droid.cbrSendTestAlert(sequence_num, 4370)
+        time.sleep(WAIT_TIME_FOR_UI)
+        # get the device time
+        stdout = ad.adb.shell("date +\"%d/%m/%Y,%I,%M,%p\"")
+        device_time = stdout.split(',')
+        title_attrs = get_element_attributes(ad, text_contains=alert_text)
+        # Verify the format(DD/MM/YYYY hh:mm PM|AM) of alert date and time.
+        format = "(\d{2}/\d{2}/\d{4}) ([0]\d|[1][012]):\d{2} (PM|AM)$"
+        alert_time = re.search(format, title_attrs["text"].value)
+        ad.log.info("The alert title is %s\nverify the format of the alert time!", title_attrs["text"].value)
+        if not alert_time:
+            result = False
+            ad.log.error("The format of the alert time is incorrect. The correct format is DD/MM/YYYY hh:mm PM|AM")
+        else:
+            ad.log.info("The format of the alert time is correct!")
+        # Verify the alert time
+        ad.log.info("The device time is %s %s:%s %s", device_time[0], device_time[1], device_time[2], device_time[3])
+        ad.log.info("Verify the alert time...")
+        # The exact matched pattern count must be 3.
+        if not alert_time or len(alert_time.groups()) != 3:
+            result = False
+            ad.log.error("The matched alert time %s is incorrect!", alert_time.group(0))
+
+        if result and (not ((device_time[0] == alert_time.group(1)
+                 and device_time[1] == alert_time.group(2)
+                 and device_time[3] == alert_time.group(3)))):
+            result = False
+            ad.log.error("The alert time is incorrect!")
+        else:
+            ad.log.info("The alert time is correct!")
+        self._exit_alert_pop_up(ad)
+
+        return result
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_allow_alerts_japan_kddi(self):
+        """ Verify alert is received after switch over 'Allow alerts' setting.
+
+        Set the device's region to Japan kddi
+        Turn off "Allow alerts" setting
+        Reboot
+        Turn on "Allow alerts" setting
+        Send channel 4353 alert
+        Verify if channel 4353 alert is received
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        ad = self.android_devices[0]
+        result = True
+        self._set_device_to_specific_region(ad, JAPAN_KDDI)
+        time.sleep(WAIT_TIME_FOR_UI)
+        ad.log.info("disable DND: %s", CMD_DND_OFF)
+        ad.adb.shell(CMD_DND_OFF)
+
+        allow_alerts_title = "Allow alerts"
+        self._open_wea_settings_page(ad)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        if not self._has_element(ad, allow_alerts_title):
+            for _ in range(3):
+                ad.adb.shell(SCROLL_DOWN)
+            if not self._has_element(ad, allow_alerts_title):
+                ad.log.error("UI - %s missing", allow_alerts_title)
+                return False
+        # Get switch button's value of Allow alerts
+        node = uutils.wait_and_get_xml_node(ad, timeout=30, text=allow_alerts_title)
+        allow_alerts_value = node.nextSibling.attributes['checked'].value
+
+        if allow_alerts_value == "true":
+            # Turn off Allow alerts
+            ad.log.info("Switch off Allow alerts!")
+            self._wait_and_click(ad, allow_alerts_title)
+        else:
+            ad.log.info("Allow alerts is off!")
+        time.sleep(WAIT_TIME_FOR_UI)
+        self._close_wea_settings_page(ad)
+
+        reboot_device(ad)
+        time.sleep(WAIT_TIME_FOR_ALERTS_TO_POPULATE)
+        self._open_wea_settings_page(ad)
+        time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
+        # Turn on Allow alerts
+        ad.log.info("Switch on Allow alerts!")
+        self._wait_and_click(ad, allow_alerts_title)
+        time.sleep(WAIT_TIME_FOR_UI)
+        self._close_wea_settings_page(ad)
+        if not self._verify_send_receive_wea_alerts(ad, JAPAN_KDDI, test_channel=4353):
+            result = False
+        get_screen_shot_log(ad)
+
+        return result
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_alert_screen_off_chile_telefonica(self):
+        """Verify the vibration is on when the screen is off after receiving alerts.
+
+        Set the device's region to Chile
+        Send channel 4378 alert
+        Turn off screen
+        Wait for alert time
+        turn on screen
+        Verify the vibration time of the alert
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        result = True
+        ad = self.android_devices[0]
+        self._set_device_to_specific_region(ad, CHILE_TELEFONICA)
+        time.sleep(WAIT_TIME_FOR_UI)
+        ad.log.info("disable DND: %s", CMD_DND_OFF)
+        ad.adb.shell(CMD_DND_OFF)
+        if not self._verify_send_receive_wea_alerts(ad, CHILE_TELEFONICA, test_channel=4378, screen_off=True):
+            result = False
+        get_screen_shot_log(ad)
+
+        return result
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_uae_upgrading_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for UAE
+
+        configures the device to UAE
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(UAE,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_australia_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for AUSTRALIA
+
+        configures the device to Australia
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_test_flow(AUSTRALIA,
+                                        upgrade_cbr_train_build=True,
+                                        rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_france_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for FRANCE
+
+        configures the device to France
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(FRANCE,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_japan_kddi_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Japan (KDDI)
+
+        configures the device to Japan (KDDI)
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(JAPAN_KDDI,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_newzealand_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for NZ
+
+        configures the device to NZ
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(NEWZEALAND,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_hongkong_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for HongKong
+
+        configures the device to HongKong
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(HONGKONG,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_chile_entel_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Chile Entel
+
+        configures the device to Chile Entel
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(CHILE_ENTEL,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_chile_telefonica_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Chile Telefonica
+
+        configures the device to Chile Telefonica
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(CHILE_TELEFONICA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_peru_entel_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Peru_Entel
+
+        configures the device to Peru_Entel
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(PERU_ENTEL,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_peru_telefonica_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Peru_Telefonica
+
+        configures the device to Peru Telefonica
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(PERU_TELEFONICA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_spain_telefonica_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Spain_Telefonica
+
+        configures the device to Spain Telefonica
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(SPAIN_TELEFONICA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_elsalvador_telefonica_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for
+            Elsalvador_Telefonica
+
+        configures the device to Elsalvador Telefonica
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(ELSALVADOR_TELEFONICA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_mexico_telefonica_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Mexico_Telefonica
+
+        configures the device to Mexico Telefonica
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(MEXICO_TELEFONICA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_korea_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Korea
+
+        configures the device to Korea
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(KOREA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_taiwan_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Taiwan
+
+        configures the device to Taiwan
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+        the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(TAIWAN,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_canada_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Canada
+
+        configures the device to Canada
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(CANADA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_brazil_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Brazil
+
+        configures the device to Brazil
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(BRAZIL,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_columbia_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Columbia
+
+        configures the device to Columbia
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(COLUMBIA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_ecuador_telefonica_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Ecuador Telefonica
+
+        configures the device to Ecuador Telefonica
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(ECUADOR_TELEFONICA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_ecuador_claro_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Ecuador Claro
+
+        configures the device to Ecuador Claro
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(ECUADOR_CLARO,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_puertorico_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Puertorico
+
+        configures the device to Puertorico
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(PUERTORICO,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_netherlands_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Netherlands
+
+        configures the device to Netherlands
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(NETHERLANDS,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_romania_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Romania
+
+        configures the device to Romania
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(ROMANIA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_estonia_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Estonia
+
+        configures the device to Estonia
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(ESTONIA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_lithuania_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Lithuania
+
+        configures the device to Lithuania
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(LITHUANIA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_latvia_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Latvia
+
+        configures the device to Latvia
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(LATVIA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_greece_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Greece
+
+        configures the device to Greece
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(GREECE,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_italy_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Italy
+
+        configures the device to Italy
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(ITALY,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_southafrica_upgrade_rollback_cbr_build(self):
+        """ Verifies wea after upgrading a new cbr build and rollback for SouthAfrica
+
+        configures the device to SouthAfrica
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(SOUTHAFRICA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_uk_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for UK
+
+        configures the device to UK
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(UK,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_israel_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Israel
+
+        configures the device to Israel
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(ISRAEL,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_oman_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Oman
+
+        configures the device to Oman
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(OMAN,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_japan_softbank_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Japan (Softbank)
+
+        configures the device to Japan (Softbank)
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(JAPAN_SOFTBANK,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_saudiarabia_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for SaudiArabia
+
+        configures the device to SaudiArabia
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(SAUDIARABIA,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_us_att_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for US ATT
+
+        configures the device to US ATT
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(US_ATT,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_us_tmo_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for US TMO
+
+        configures the device to US TMO
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(US_TMO,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_us_vzw_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for US VZW
+
+        configures the device to US VZW
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(US_VZW,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_germany_telekom_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Germany telecom
+
+        configures the device to Germany telecom
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(GERMANY_TELEKOM,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
+    @TelephonyBaseTest.tel_test_wrap
+    def test_default_alert_settings_qatar_vodafone_upgrade_rollback_cbr_build(self):
+        """ Verifies wea settings after upgrading a new cbr build and rollback for Qatar vodafone
+
+        configures the device to Qatar vodafone
+        upgrades a new cbr build
+        reports errors if the versions of the upgraded cbr build and
+            the factory cbr build are the same
+        verifies alert names and its default values
+        toggles the alert twice if available
+        rolls back the factory cbr build
+        reports errors if the versions of the factory cbr build and
+            the rollback cbr build are different
+        verifies alert names and its default values
+        toggles the alert twice if available
+
+        Returns:
+            True if pass; False if fail and collects screenshot
+        """
+        return self._settings_upgrade_cbr_test_flow(QATAR_VODAFONE,
+                                                    upgrade_cbr_train_build=True,
+                                                    rollback_cbr_train_build=True)
+
+
diff --git a/acts_tests/tests/google/nr/nsa5g/Nsa5gActivationTest.py b/acts_tests/tests/google/nr/nsa5g/Nsa5gActivationTest.py
index eab184d..2fa6c31 100755
--- a/acts_tests/tests/google/nr/nsa5g/Nsa5gActivationTest.py
+++ b/acts_tests/tests/google/nr/nsa5g/Nsa5gActivationTest.py
@@ -17,8 +17,6 @@
     Test Script for 5G Activation scenarios
 """
 
-import time
-
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
@@ -37,10 +35,8 @@
     def teardown_class(self):
         TelephonyBaseTest.teardown_class(self)
 
-
     """ Tests Begin """
 
-
     @test_tracker_info(uuid="b10b3779-b535-4cac-a12f-2fb6daed55a5")
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_nsa_activation_from_apm(self):
@@ -55,10 +51,10 @@
             True if pass; False if fail.
         """
 
-        return test_activation_by_condition(self.android_devices[0],
-                                            nr_type='nsa',
-                                            precond_func=lambda: cycle_airplane_mode(self.android_devices[0]))
-
+        return test_activation_by_condition(
+            self.android_devices[0],
+            nr_type='nsa',
+            precond_func=lambda: cycle_airplane_mode(self.android_devices[0]))
 
     @test_tracker_info(uuid="d4f5f0c5-cc58-4531-96dd-32eed9121b95")
     @TelephonyBaseTest.tel_test_wrap
@@ -74,10 +70,10 @@
             True if pass; False if fail.
         """
 
-        return test_activation_by_condition(self.android_devices[0],
-                                            nr_type='nsa',
-                                            precond_func=lambda: reboot_device(self.android_devices[0]))
-
+        return test_activation_by_condition(
+            self.android_devices[0],
+            nr_type='nsa',
+            precond_func=lambda: reboot_device(self.android_devices[0]))
 
     @test_tracker_info(uuid="1ceda4b5-4a6a-43fa-8976-67cbfb7eab5b")
     @TelephonyBaseTest.tel_test_wrap
diff --git a/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSDDSSwitchTest.py b/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSDDSSwitchTest.py
index 9c2ddce..16196d7 100644
--- a/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSDDSSwitchTest.py
+++ b/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSDDSSwitchTest.py
@@ -14,8 +14,11 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import time
+
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
+from acts_contrib.test_utils.tel.tel_defines import SimSlotInfo
 from acts_contrib.test_utils.tel.tel_dsds_utils import dds_switch_during_data_transfer_test
 from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_dds_swap_call_streaming_test
 from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_dds_swap_message_streaming_test
@@ -23,11 +26,28 @@
 from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 
+_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL = 60
+_WAIT_TIME_FOR_MEP_ENABLE = 180
+
+
 class Nsa5gDSDSDDSSwitchTest(TelephonyBaseTest):
     def setup_class(self):
         TelephonyBaseTest.setup_class(self)
         self.message_lengths = (50, 160, 180)
         self.tel_logger = TelephonyMetricLogger.for_test_case()
+        if getattr(self.android_devices[0], 'mep', False):
+            start_time = time.monotonic()
+            timeout = start_time + _WAIT_TIME_FOR_MEP_ENABLE
+            while time.monotonic() < timeout:
+                mep_logs = self.android_devices[0].search_logcat(
+                    "UNSOL_SIM_SLOT_STATUS_CHANGED")
+                if mep_logs:
+                    for mep_log in mep_logs:
+                        if "num_ports=2" in mep_log["log_message"]:
+                            break
+                time.sleep(_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL)
+            else:
+                self.log.warning("Couldn't found MEP enabled logs.")
 
     def teardown_test(self):
         self.android_devices[0].force_stop_apk(YOUTUBE_PACKAGE_NAME)
@@ -49,8 +69,12 @@
         return dsds_dds_swap_message_streaming_test(
             self.log,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[0, 1],
+            test_slot=[
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_0],
             init_dds=0,
             msg_type="SMS",
             direction="mt",
@@ -72,8 +96,12 @@
         return dsds_dds_swap_message_streaming_test(
             self.log,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[1, 0],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_1],
             init_dds=0,
             msg_type="SMS",
             direction="mt",
@@ -99,8 +127,12 @@
         return dsds_dds_swap_message_streaming_test(
             self.log,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[0, 1],
+            test_slot=[
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_0],
             init_dds=0,
             msg_type="SMS",
             direction="mt",
@@ -126,8 +158,12 @@
         return dsds_dds_swap_message_streaming_test(
             self.log,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[1, 0],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_1],
             init_dds=0,
             msg_type="SMS",
             direction="mt",
@@ -149,8 +185,12 @@
         return dsds_dds_swap_message_streaming_test(
             self.log,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[0, 1],
+            test_slot=[
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_0],
             init_dds=0,
             msg_type="MMS",
             direction="mt",
@@ -172,8 +212,12 @@
         return dsds_dds_swap_message_streaming_test(
             self.log,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[1, 0],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_1],
             init_dds=0,
             msg_type="MMS",
             direction="mt",
@@ -199,8 +243,12 @@
         return dsds_dds_swap_message_streaming_test(
             self.log,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[0, 1],
+            test_slot=[
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_0],
             init_dds=0,
             msg_type="MMS",
             direction="mt",
@@ -226,8 +274,12 @@
         return dsds_dds_swap_message_streaming_test(
             self.log,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[1, 0],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_1],
             init_dds=0,
             msg_type="MMS",
             direction="mt",
@@ -250,9 +302,13 @@
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[0, 0],
             init_dds=0,
+            test_slot=[
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_0],
             direction="mo",
             duration=30,
             streaming=False)
@@ -274,9 +330,13 @@
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[0, 0],
             init_dds=0,
+            test_slot=[
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_0,
+                SimSlotInfo.SLOT_0],
             direction="mt",
             duration=30,
             streaming=False)
@@ -298,9 +358,13 @@
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[1, 1],
             init_dds=0,
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
             direction="mo",
             duration=30,
             streaming=False)
@@ -322,9 +386,13 @@
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=[1, 1],
             init_dds=0,
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
             direction="mt",
             duration=30,
             streaming=False)
@@ -332,11 +400,14 @@
     @test_tracker_info(uuid="727a75ef-7277-42fe-8a4b-7b2debe666d9")
     @TelephonyBaseTest.tel_test_wrap
     def test_dds_switch_youtube_psim_5g_nsa_volte_esim_5g_nsa_volte(self):
-        return dds_switch_during_data_transfer_test(
+        return dsds_dds_swap_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            nw_rat=["5g_volte", "5g_volte"])
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            init_dds=0,
+            test_slot=[None, None, None])
 
     @test_tracker_info(uuid="4ef4626a-11b3-4a09-ac98-2e3d94e54bf7")
     @TelephonyBaseTest.tel_test_wrap
@@ -425,3 +496,352 @@
             nw_rat=["5g_csfb", "5g_csfb"],
             call_slot=1,
             call_direction="mt")
+
+    # e+e call
+    @test_tracker_info(uuid="82170198-a3c8-46b5-9fee-d4284d69a4c1")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_youtube_esim_port_0_5g_nsa_volte_esim_port_1_5g_nsa_volte(self):
+        """ 5G NSA DDS swap call test(Initial DDS is on esim port 0).
+
+        1. Check HTTP connection when DDS is on esim port 0 and idle.
+        2. Switch DDS to esim port 1.
+        3. Check HTTP connection when DDS is on esim port 1 and idle.
+        4. Switch DDS to esim port 0, make sure data works fine.
+        """
+        return dsds_dds_swap_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            init_dds=1,
+            test_slot=[None, None, None])
+
+    @test_tracker_info(uuid="873dd4cc-0439-483c-94c0-0756d8b7a777")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_voice_esim_port_0_mo_5g_nsa_volte_esim_port_1_5g_nsa_volte(self):
+        """ 5G NSA DDS swap call test(Initial DDS is on esim port 0).
+
+        1. Make MO call via esim port 0 when DDS is on esim port 0 and idle.
+        2. Switch DDS to esim port 1.
+        3. Make MO call via esim port 0 when DDS is on esim port 1 and idle.
+        4. Switch DDS to esim port 0, make sure data works fine.
+
+        After call end will check the dds slot if is attach to the network
+        with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            init_dds=1,
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            direction="mo",
+            duration=30,
+            streaming=False)
+
+    @test_tracker_info(uuid="56b080cf-729b-469c-8738-b69a67eabf6e")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_voice_esim_port_0_mt_5g_nsa_volte_esim_port_1_5g_nsa_volte(self):
+        """ 5G NSA DDS swap call test(Initial DDS is on esim port 0).
+
+        1. Receive MT call via esim port 0 when DDS is on esim port 0 and idle.
+        2. Switch DDS to esim port 1.
+        3. Receive MT call via esim port 0 when DDS is on esim port 1 and idle.
+        4. Switch DDS to esim port 0, make sure data works fine.
+
+        After call end will check the dds slot if is attach to the network
+        with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            init_dds=1,
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            direction="mt",
+            duration=30,
+            streaming=False)
+
+    @test_tracker_info(uuid="bbccecf6-f691-4bde-9c20-56bdd5aeb033")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_voice_esim_port_1_mo_5g_nsa_volte_esim_port_0_5g_nsa_volte(self):
+        """ 5G NSA DDS swap call test(Initial DDS is on esim port 0).
+
+        1. Make MO call via esim port 1 when DDS is on esim port 0 and idle.
+        2. Switch DDS to esim port 1.
+        3. Make MO call via esim port 1 when DDS is on esim port 1 and idle.
+        4. Switch DDS to esim port 0, make sure data works fine.
+
+        After call end will check the dds slot if is attach to the network
+        with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            init_dds=1,
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            direction="mo",
+            duration=30,
+            streaming=False)
+
+    @test_tracker_info(uuid="ce848306-38b7-4479-bf97-8413ec18dee8")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_voice_esim_port_1_mt_5g_nsa_volte_esim_port_0_5g_nsa_volte(self):
+        """ 5G NSA DDS swap call test(Initial DDS is on esim port 0).
+
+        1. Receive MT call via esim port 1 when DDS is on esim port 0 and idle.
+        2. Switch DDS to esim port 1.
+        3. Receive MT call via esim port 1 when DDS is on esim port 1 and idle.
+        4. Switch DDS to esim port 0, make sure data works fine.
+
+        After call end will check the dds slot if is attach to the network
+        with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            init_dds=1,
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            direction="mt",
+            duration=30,
+            streaming=False)
+
+    # e+e message
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_sms_esim_port_0_mo_5g_nsa_volte_esim_port_1_5g_nsa_volte(self):
+        """ 5G NSA DDS swap SMS test(Initial DDS is on esim_port_0).
+
+        1. Make MO SMS via esim_port_0 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MO SMS via esim_port_0 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make SMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            init_dds=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_sms_esim_port_0_mt_5g_nsa_volte_esim_port_1_5g_nsa_volte(self):
+        """ 5G NSA DDS swap SMS test(Initial DDS is on esim_port_0).
+
+        1. Make MT SMS via esim_port_0 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MT SMS via esim_port_0 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Receive SMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            init_dds=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_sms_esim_port_1_mo_5g_nsa_volte_esim_port_0_5g_nsa_volte(self):
+        """ 5G NSA DDS swap SMS test(Initial DDS is on esim_port_0).
+
+        1. Make MO SMS via esim_port_1 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MO SMS via esim_port_1 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make SMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            init_dds=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_sms_esim_port_1_mt_5g_nsa_volte_esim_port_0_5g_nsa_volte(self):
+        """ 5G NSA DDS swap SMS test(Initial DDS is on esim_port_0).
+
+        1. Make MT SMS via esim_port_1 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MT SMS via esim_port_1 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make SMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            init_dds=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_mms_esim_port_0_mo_5g_nsa_volte_esim_port_1_5g_nsa_volte(self):
+        """ 5G NSA DDS swap MMS test(Initial DDS is on esim_port_0).
+
+        1. Make MO MMS via esim_port_0 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MO MMS via esim_port_0 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make MMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            init_dds=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_mms_esim_port_0_mt_5g_nsa_volte_esim_port_1_5g_nsa_volte(self):
+        """ 5G NSA DDS swap MMS test(Initial DDS is on esim_port_0).
+
+        1. Make MT MMS via esim_port_0 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MT MMS via esim_port_0 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Receive MMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            init_dds=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_mms_esim_port_1_mo_5g_nsa_volte_esim_port_0_5g_nsa_volte(self):
+        """ 5G NSA DDS swap MMS test(Initial DDS is on esim_port_0).
+
+        1. Make MO MMS via esim_port_1 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MO MMS via esim_port_1 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make MMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            init_dds=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_mms_esim_port_1_mt_5g_nsa_volte_esim_port_0_5g_nsa_volte(self):
+        """ 5G NSA DDS swap MMS test(Initial DDS is on esim_port_0).
+
+        1. Make MT MMS via esim_port_1 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MT MMS via esim_port_1 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make MMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            init_dds=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
\ No newline at end of file
diff --git a/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSMessageTest.py b/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSMessageTest.py
index a21cde3..371ae23 100644
--- a/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSMessageTest.py
+++ b/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSMessageTest.py
@@ -14,20 +14,39 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import time
+
 from acts.test_decorators import test_tracker_info
 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 SimSlotInfo
 from acts_contrib.test_utils.tel.tel_defines import YOUTUBE_PACKAGE_NAME
-from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_message_test
+from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_message_streaming_test
 from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 
+_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL = 60
+_WAIT_TIME_FOR_MEP_ENABLE = 180
+
 CallResult = TelephonyVoiceTestResult.CallResult.Value
 
+
 class Nsa5gDSDSMessageTest(TelephonyBaseTest):
     def setup_class(self):
         TelephonyBaseTest.setup_class(self)
         self.tel_logger = TelephonyMetricLogger.for_test_case()
+        if getattr(self.android_devices[0], 'mep', False):
+            start_time = time.monotonic()
+            timeout = start_time + _WAIT_TIME_FOR_MEP_ENABLE
+            while time.monotonic() < timeout:
+                mep_logs = self.android_devices[0].search_logcat("UNSOL_SIM_SLOT_STATUS_CHANGED")
+                if mep_logs:
+                    for mep_log in mep_logs:
+                        if "num_ports=2" in mep_log["log_message"]:
+                            break
+                time.sleep(_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL)
+            else:
+                self.log.warning("Couldn't found MEP enabled logs.")
 
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
@@ -35,767 +54,1568 @@
     @test_tracker_info(uuid="123a50bc-f0a0-4129-9377-cc63c76d5727")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="5dcf76bc-369f-4d47-b3ec-318559a95843")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="245a6148-cd45-4b82-bf4c-5679ebe15e29")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="5a93d377-d9bc-477c-bfab-2496064e3522")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="dd4a9fb5-b0fe-492b-ad24-61e022d13a22")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="09100a8f-b7ed-41a0-9f04-e716115cabb8")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="b5971c57-bbe9-4e87-a6f2-9953fa770a15")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="142b11d4-b593-4a09-8fc6-35e310739244")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="87759475-0208-4d9b-b5b9-814fdb97f09c")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_psim_5g_nsa_volte_esim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["5g_volte", "volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="2f14e81d-330f-4cdd-837c-1168185ffec4")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_psim_5g_nsa_volte_esim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["5g_volte", "volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="38f01127-54bf-4c55-b7d8-d8f41352b399")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_psim_5g_nsa_volte_esim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["5g_volte", "volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="4e0c9692-a758-4169-85fe-c33bd2651525")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_psim_5g_nsa_volte_esim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["5g_volte", "volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="9cc45474-1fca-4008-8499-87829d6516ea")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_esim_4g_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["5g_volte", "volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="341786de-5b23-438a-a91b-97cf420ef5fd")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_esim_4g_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["5g_volte", "volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="527e8629-6e0d-4742-98c0-5cbc868c430e")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_esim_4g_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["5g_volte", "volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="66277aa0-0a9a-4a25-828f-b0315ae7fd0e")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_esim_4g_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["5g_volte", "volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="a4d797b6-2699-48de-b36b-b10a1901305b")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_psim_4g_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="371286ba-f1da-4459-a7e8-0368d0fae147")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_psim_4g_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="183cda35-45aa-485d-b3d4-975d78f7d361")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_psim_4g_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="d9cb69ce-c462-4fd4-b716-bfb1fd2ed86a")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_psim_4g_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="dfe54cea-8396-4af4-8aee-9dadad602e5b")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_esim_5g_nsa_volte_psim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="fd4ae44c-3527-4b90-8d33-face10e160a6")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_esim_5g_nsa_volte_psim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="51d5e05d-66e7-4369-91e0-6cdc573d9a59")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_esim_5g_nsa_volte_psim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="38271a0f-2efb-4991-9f24-6da9f003ddd4")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_esim_5g_nsa_volte_psim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="dde1e900-abcd-4a5f-8872-02456ea248ee")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="5a8ad6dc-687a-498e-8b99-119f3cbb781c")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="765443f4-d4a0-45fe-8c97-763feb4b588b")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="026b9e8f-400e-4b59-b40d-d4e741838be0")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="468536c1-de6e-48e7-b59e-11f17389ac12")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="c4ae7f6b-bc20-4cb2-8e41-8a02171aec6f")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="2d70443e-b442-48e0-9c1f-ce1409184ff8")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="47fbc6c0-ca76-44c0-a166-d8c99d16b6ac")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="39684fbc-73d1-48cb-af3f-07a366a6b190")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_psim_5g_nsa_volte_esim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["5g_volte", "volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="6adf4163-4969-4129-bbac-4ebdac4c4cf5")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_psim_5g_nsa_volte_esim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["5g_volte", "volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="7b636038-5b0c-4844-ba2a-2e76ed787f72")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_psim_5g_nsa_volte_esim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["5g_volte", "volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="b5008ad4-372d-4849-b47b-583be6aa080a")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_psim_5g_nsa_volte_esim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["5g_volte", "volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="fd6b33b6-c654-4ec0-becc-2fd7ec10c291")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_esim_4g_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["5g_volte", "volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="0267c4e8-e5b8-4001-912f-76c387a15f79")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_esim_4g_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["5g_volte", "volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="a54caa16-dfc6-46e1-a376-b4b585e2e840")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_esim_4g_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["5g_volte", "volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="f6af184a-933b-467e-81a7-44ef48b56540")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_esim_4g_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["5g_volte", "volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="2e89f125-aacc-4c36-a1c2-308cd83b0e22")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_psim_4g_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="03c23c94-3cc5-4ecf-9b87-273c815b9f53")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_psim_4g_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="d2af382a-0f87-46c0-b2de-84f5a549e32c")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_psim_4g_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="bf788d99-954b-47e2-b465-8565bb30e907")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_psim_4g_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="f0b1d46b-6ddc-4625-b653-38e323e542ad")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_esim_5g_nsa_volte_psim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="b17a4943-1f69-428a-bd63-13144b2bc592")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_esim_5g_nsa_volte_psim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="98d7b7b8-0bd3-4362-957b-56c8b19ac3d4")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_esim_5g_nsa_volte_psim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="5f0f4174-548d-43ab-b520-5e2211fdaacc")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_esim_5g_nsa_volte_psim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="09cd2c80-5c94-4b97-badd-b9d23712cbad")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mo", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="deed7037-932e-4c08-bbf0-989144a51193")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mt", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="14fe5ef1-e6aa-4615-887a-ac26043c2dfc")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mo", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="1f07d373-dc81-42f4-a5c5-461304f1e7bf")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mt", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="a9f066d3-a5db-4319-a5c9-f7a20f84cd6e")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mo", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="688485af-cdc7-43b7-af01-baf6bc695b70")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mt", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="7fef6173-1f37-45d3-be94-60fea340444c")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mo", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="71b15942-6c8f-41b3-8dc9-5a1dea64aad4")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["5g_volte", "5g_volte"], msg="SMS", direction="mt", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="6cbc50e7-e135-405d-bf69-ab074d345d80")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_psim_5g_nsa_volte_esim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["5g_volte", "volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="d976560a-1ea1-421a-9c2d-906cbfb7654e")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_psim_5g_nsa_volte_esim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["5g_volte", "volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="0e3a10b2-2351-49a2-9282-99aae1372bf0")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_psim_5g_nsa_volte_esim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["5g_volte", "volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="e713c430-0bfa-4d25-91f3-1b6fec84b3a5")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_psim_5g_nsa_volte_esim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["5g_volte", "volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="770bec4d-c1c9-4936-8683-3fb796827eba")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_esim_4g_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["5g_volte", "volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="3f34328b-9295-4740-a48b-3ffadbab3fb5")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_esim_4g_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["5g_volte", "volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="eeaeb58a-7566-498e-a4d1-ce1cbd82f362")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_esim_4g_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["5g_volte", "volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="7550ef0b-b0d3-4932-95d3-119abdad53ad")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_esim_4g_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["5g_volte", "volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="6dd693f4-6c61-4048-9027-02c17874dbd0")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_psim_4g_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="976d5c30-63af-4e49-952e-2cd4147b7c8d")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_psim_4g_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="b2c94d26-c806-417d-a751-618491dce246")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_psim_4g_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="02739364-2848-4242-bb6e-41a03ec358ed")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_psim_4g_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="811880fd-c422-4548-8dfb-cddbfb1dc6c0")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_esim_5g_nsa_volte_psim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="9e02ade7-c2b6-4b7e-ab15-b42c119f4141")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_esim_5g_nsa_volte_psim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="ba2ce2de-a0a6-4abe-adb8-110541e60cb1")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mo_esim_5g_nsa_volte_psim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["volte", "5g_volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="46e1397c-7296-4aac-8e0f-7049d04427bc")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_sms_mt_esim_5g_nsa_volte_psim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["volte", "5g_volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="181c1ac9-625e-450d-b566-834e20ecd59d")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mo", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="b37aceed-7f67-4ae3-aba8-0f94d24d81e2")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mt", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="0fb13f48-bfd7-4019-8a33-e229677b3357")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mo", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="016369fa-3420-45f5-9ed2-3776816f4e4b")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_psim_5g_nsa_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mt", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="65fdbecf-9ea5-4881-9e99-4a1ed90b76cc")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mo", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="3e6b4bcf-30cd-4502-8811-2a5a7a9142a5")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mt", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="a49b7a91-8811-403b-b8ed-ac0edad69c2c")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mo", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="961db859-ad50-4b13-8555-e523843d3e0c")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_esim_5g_nsa_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["5g_volte", "5g_volte"], msg="MMS", direction="mt", streaming=True)
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="398fea0a-4ef4-4a6d-bea0-76ab0b2e2c34")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_psim_5g_nsa_volte_esim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["5g_volte", "volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="06503954-caff-47ba-8ed3-7793fca4e94a")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_psim_5g_nsa_volte_esim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["5g_volte", "volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="bc43d539-7bbd-4b12-b88a-ecf0229f1ed5")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_psim_5g_nsa_volte_esim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["5g_volte", "volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="d558a53b-396e-4a9e-aec1-929f41f8ad2a")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_psim_5g_nsa_volte_esim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["5g_volte", "volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="74afcd0a-e121-4028-99c6-48cad25b18b8")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_esim_4g_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["5g_volte", "volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="5042ec42-f1b3-466a-8e06-6e1c3de2dffb")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_esim_4g_volte_psim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["5g_volte", "volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="6f286c93-004b-4360-9afa-78f15a0a5549")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_esim_4g_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["5g_volte", "volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="0313548b-653b-44ea-bb63-76b69b67e456")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_esim_4g_volte_psim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["5g_volte", "volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="4c1e4667-2b0d-4f4d-a419-c349ef767dbc")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_psim_4g_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 0, mo_rat=["volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="f82650db-d0d9-4990-a3c6-b918eabeddc6")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_psim_4g_volte_esim_5g_nsa_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 0, mt_rat=["volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="76dca39c-8ead-435b-8b5f-8b167946a18e")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_psim_4g_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="29d8ffec-be68-4d12-b2ad-b2e8f95347c1")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_psim_4g_volte_esim_5g_nsa_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="625bc42e-c9c7-442e-8464-72aab6055ef8")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_esim_5g_nsa_volte_psim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="18f852da-0877-4624-bbcd-d59a168780dc")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_esim_5g_nsa_volte_psim_4g_volte_dds_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["volte", "5g_volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
 
     @test_tracker_info(uuid="cac044ec-176d-4eef-885d-ba419ab634eb")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mo_esim_5g_nsa_volte_psim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 1, mo_rat=["volte", "5g_volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=True)
 
     @test_tracker_info(uuid="245ee61e-f768-403c-9005-7eed90deedd7")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_youtube_and_mms_mt_esim_5g_nsa_volte_psim_4g_volte_dds_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 1, mt_rat=["volte", "5g_volte"], msg="MMS", direction="mt")
\ No newline at end of file
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=True)
+
+    # e+e
+    @test_tracker_info(uuid="3233f15d-0b42-4215-88af-ccb78bce5cc1")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mo_esim_port_0_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="213be122-d935-49c2-9db5-cb1828c23687")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mt_esim_port_0_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="22441a1d-0152-4870-a14c-7f733e82b417")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mo_esim_port_1_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=2,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="cf6871f4-8ad2-4f2a-8ae0-72ee030e42cd")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mt_esim_port_1_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=2,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="56ec2aa6-e3ca-4f1b-8167-6276842610d2")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mo_esim_port_1_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="a185e6f8-fb1b-4876-84ed-fab6c51010ca")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mt_esim_port_1_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="3089b689-49e7-4e63-b931-5073c323411d")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mo_esim_port_0_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=2,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="2b6a9461-0a8c-4452-beac-f133fb0e383b")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mt_esim_port_0_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=2,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="b1da493c-aa11-4bf4-814d-ecd2fdffb835")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mo_esim_port_0_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="7bbd85ca-7484-4850-99c9-00761715af5f")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mt_esim_port_0_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="02d00988-5d41-467b-9eaa-abb84a5e89f6")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mo_esim_port_1_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=2,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="fc3ce672-d9a8-4346-b46d-bbc9c0c8db35")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mt_esim_port_1_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=2,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="121ca139-e457-49f6-ba0e-ffc2008ccdd8")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mo_esim_port_1_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="8cb07ef0-b421-43d2-80a7-48440975dc2c")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mt_esim_port_1_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="43ae713b-3309-47b9-8036-59f5dd124dab")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mo_esim_port_0_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=2,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="3ad59561-5860-4c8d-87f5-adf5af973757")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mt_esim_port_0_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=2,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
\ No newline at end of file
diff --git a/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSSupplementaryServiceTest.py b/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSSupplementaryServiceTest.py
index 2b06b2c..616b0d7 100644
--- a/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSSupplementaryServiceTest.py
+++ b/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSSupplementaryServiceTest.py
@@ -14,9 +14,12 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import time
+
 from acts import signals
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
+from acts_contrib.test_utils.tel.tel_defines import SimSlotInfo
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
 from acts_contrib.test_utils.tel.tel_dsds_utils import erase_call_forwarding
@@ -27,12 +30,29 @@
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
 from acts_contrib.test_utils.tel.tel_test_utils import get_capability_for_subscription
 
+_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL = 60
+_WAIT_TIME_FOR_MEP_ENABLE = 180
+
 
 class Nsa5gDSDSSupplementaryServiceTest(TelephonyBaseTest):
     def setup_class(self):
         TelephonyBaseTest.setup_class(self)
         self.message_lengths = (50, 160, 180)
         self.tel_logger = TelephonyMetricLogger.for_test_case()
+        if getattr(self.android_devices[0], 'mep', False):
+            start_time = time.monotonic()
+            timeout = start_time + _WAIT_TIME_FOR_MEP_ENABLE
+            while time.monotonic() < timeout:
+                mep_logs = self.android_devices[0].search_logcat(
+                    "UNSOL_SIM_SLOT_STATUS_CHANGED")
+                if mep_logs:
+                    for mep_log in mep_logs:
+                        if "num_ports=2" in mep_log["log_message"]:
+                            break
+                time.sleep(_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL)
+            else:
+                self.log.warning("Couldn't found MEP enabled logs.")
+
         erase_call_forwarding(self.log, self.android_devices[0])
         if not get_capability_for_subscription(
             self.android_devices[0],
@@ -76,6 +96,7 @@
             0,
             None,
             0,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["5g_volte", "5g_volte"],
             call_forwarding_type="unconditional")
 
@@ -106,6 +127,7 @@
             1,
             None,
             0,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["5g_volte", "5g_volte"],
             call_forwarding_type="unconditional")
 
@@ -137,6 +159,7 @@
             0,
             None,
             1,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["5g_volte", "5g_volte"],
             call_forwarding_type="unconditional")
 
@@ -167,6 +190,7 @@
             1,
             None,
             1,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["5g_volte", "5g_volte"],
             call_forwarding_type="unconditional")
 
@@ -198,6 +222,7 @@
             0,
             None,
             0,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["5g_volte", "volte"],
             call_forwarding_type="unconditional")
 
@@ -228,6 +253,7 @@
             1,
             None,
             0,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["5g_volte", "volte"],
             call_forwarding_type="unconditional")
 
@@ -259,6 +285,7 @@
             0,
             None,
             1,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["5g_volte", "volte"],
             call_forwarding_type="unconditional")
 
@@ -289,6 +316,7 @@
             1,
             None,
             1,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["5g_volte", "volte"],
             call_forwarding_type="unconditional")
 
@@ -319,6 +347,7 @@
             0,
             None,
             0,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["volte", "5g_volte"],
             call_forwarding_type="unconditional")
 
@@ -349,6 +378,7 @@
             1,
             None,
             0,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["volte", "5g_volte"],
             call_forwarding_type="unconditional")
 
@@ -379,6 +409,7 @@
             0,
             None,
             1,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["volte", "5g_volte"],
             call_forwarding_type="unconditional")
 
@@ -409,6 +440,7 @@
             1,
             None,
             1,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             callee_rat=["volte", "5g_volte"],
             call_forwarding_type="unconditional")
 
@@ -1005,7 +1037,7 @@
             1,
             host_rat=["5g_volte", "volte"],
             merge=False, disable_cw=False):
-        	result = False
+            result = False
         if not msim_call_voice_conf(
             self.log,
             self.tel_logger,
@@ -1017,7 +1049,7 @@
             host_rat=["5g_volte", "volte"],
             merge=False,
             disable_cw=True):
-        	result = False
+            result = False
         return result
 
     @TelephonyBaseTest.tel_test_wrap
diff --git a/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSVoiceTest.py b/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSVoiceTest.py
index 3544b8d..a1c7f0f 100644
--- a/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSVoiceTest.py
+++ b/acts_tests/tests/google/nr/nsa5g/Nsa5gDSDSVoiceTest.py
@@ -14,22 +14,42 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import time
+
+from acts import signals
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
+from acts_contrib.test_utils.tel.tel_defines import SimSlotInfo
 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_PREFERRED
-from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_long_call_streaming_test
+from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_call_streaming_test
 from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_voice_call_test
 from acts_contrib.test_utils.tel.tel_dsds_utils import enable_slot_after_voice_call_test
 from acts_contrib.test_utils.tel.tel_dsds_utils import enable_slot_after_data_call_test
 from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
 
+_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL = 60
+_WAIT_TIME_FOR_MEP_ENABLE = 180
+
 
 class Nsa5gDSDSVoiceTest(TelephonyBaseTest):
     def setup_class(self):
         TelephonyBaseTest.setup_class(self)
         self.tel_logger = TelephonyMetricLogger.for_test_case()
+        if getattr(self.android_devices[0], 'mep', False):
+            start_time = time.monotonic()
+            timeout = start_time + _WAIT_TIME_FOR_MEP_ENABLE
+            while time.monotonic() < timeout:
+                mep_logs = self.android_devices[0].search_logcat(
+                    "UNSOL_SIM_SLOT_STATUS_CHANGED")
+                if mep_logs:
+                    for mep_log in mep_logs:
+                        if "num_ports=2" in mep_log["log_message"]:
+                            break
+                time.sleep(_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL)
+            else:
+                self.log.warning("Couldn't found MEP enabled logs.")
 
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
@@ -43,15 +63,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            0,
-            None,
-            0,
-            mo_rat=["5g_volte", "5g_volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="b05b6aea-7c48-4412-b0b1-f57192fc786c")
     @TelephonyBaseTest.tel_test_wrap
@@ -61,15 +82,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            0,
-            0,
-            mt_rat=["5g_volte", "5g_volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="213d5e6f-97df-4c2a-9745-4e40a704853a")
     @TelephonyBaseTest.tel_test_wrap
@@ -79,15 +101,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            1,
-            None,
-            0,
-            mo_rat=["5g_volte", "5g_volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="48a06a2f-b3d0-4b0e-85e5-2d439ee3147b")
     @TelephonyBaseTest.tel_test_wrap
@@ -97,15 +120,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            1,
-            0,
-            mt_rat=["5g_volte", "5g_volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
 
     # psim 5g nsa volte & esim 5g nsa volte & dds slot 1
     @test_tracker_info(uuid="406bd5e5-b549-470d-b15a-20b4bb5ff3db")
@@ -116,15 +140,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at pSIM (slot 1)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            0,
-            None,
-            1,
-            mo_rat=["5g_volte", "5g_volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="a1e52cee-78ab-4d6e-859b-faf542b8056b")
     @TelephonyBaseTest.tel_test_wrap
@@ -134,15 +159,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at pSIM (slot 1)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            0,
-            1,
-            mt_rat=["5g_volte", "5g_volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="3b9d796c-b658-4bff-aae0-1243ce8c3d54")
     @TelephonyBaseTest.tel_test_wrap
@@ -152,15 +178,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at pSIM (slot 1)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            1,
-            None,
-            1,
-            mo_rat=["5g_volte", "5g_volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="e3edd065-72e1-4067-901c-1454706e9f43")
     @TelephonyBaseTest.tel_test_wrap
@@ -170,15 +197,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at pSIM (slot 1)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            1,
-            1,
-            mt_rat=["5g_volte", "5g_volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
 
     # psim 5g nsa volte & esim 4g volte & dds slot 0
     @test_tracker_info(uuid="2890827d-deb2-42ea-921d-3b45f7645d61")
@@ -189,15 +217,16 @@
             - eSIM 4G VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            0,
-            None,
-            0,
-            mo_rat=["5g_volte", "volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="83d9b127-25da-4c19-a3a0-470a5ced020b")
     @TelephonyBaseTest.tel_test_wrap
@@ -207,15 +236,16 @@
             - eSIM 4G VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            0,
-            0,
-            mt_rat=["5g_volte", "volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="14c29c79-d100-4f03-b3df-f2ae4a172cc5")
     @TelephonyBaseTest.tel_test_wrap
@@ -225,15 +255,16 @@
             - eSIM 4G VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            1,
-            None,
-            0,
-            mo_rat=["5g_volte", "volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="12a59cc1-8c1e-44a0-836b-0d842c0746a3")
     @TelephonyBaseTest.tel_test_wrap
@@ -243,15 +274,16 @@
             - eSIM 4G VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            1,
-            0,
-            mt_rat=["5g_volte", "volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
 
     # psim 5g nsa volte & esim 4g volte & dds slot 1
     @test_tracker_info(uuid="9dfa66cc-f464-4964-9e5a-07e01d3e263e")
@@ -262,15 +294,16 @@
             - eSIM 4G VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            0,
-            None,
-            1,
-            mo_rat=["5g_volte", "volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="97e9ecc0-e377-46a8-9b13-ecedcb98922b")
     @TelephonyBaseTest.tel_test_wrap
@@ -280,15 +313,16 @@
             - eSIM 4G VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            0,
-            1,
-            mt_rat=["5g_volte", "volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="5814cd18-e33b-45c5-b129-bec7e3992d8e")
     @TelephonyBaseTest.tel_test_wrap
@@ -298,15 +332,16 @@
             - eSIM 4G VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            1,
-            None,
-            1,
-            mo_rat=["5g_volte", "volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="457dd160-f7b1-4cfd-920f-1f5ab64f6d78")
     @TelephonyBaseTest.tel_test_wrap
@@ -316,15 +351,16 @@
             - eSIM 4G VoLTE
             - DDS at pSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            1,
-            1,
-            mt_rat=["5g_volte", "volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
 
     # psim 4g volte & esim 5g nsa volte & dds slot 0
     @test_tracker_info(uuid="db5fca13-bcd8-420b-9953-256186efa290")
@@ -335,15 +371,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at eSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            0,
-            None,
-            0,
-            mo_rat=["volte", "5g_volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="2fe76eda-20b2-46ab-a1f4-c2c2bc501f38")
     @TelephonyBaseTest.tel_test_wrap
@@ -353,15 +390,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at eSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            0,
-            0,
-            mt_rat=["volte", "5g_volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="90005074-e21f-47c3-9965-54b513214600")
     @TelephonyBaseTest.tel_test_wrap
@@ -371,15 +409,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at eSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            1,
-            None,
-            0,
-            mo_rat=["volte", "5g_volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="eaf94a45-66d0-41d0-8cb2-153fa3f751f9")
     @TelephonyBaseTest.tel_test_wrap
@@ -389,15 +428,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at eSIM (slot 0)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            1,
-            0,
-            mt_rat=["volte", "5g_volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
 
     # psim 4g volte & esim 5g nsa volte & dds slot 1
     @test_tracker_info(uuid="8ee47ad7-24b6-4cd3-9443-6ab677695eb7")
@@ -408,15 +448,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at eSIM (slot 1)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            0,
-            None,
-            1,
-            mo_rat=["volte", "5g_volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="8795b95d-a138-45cd-b45c-41ad4021589a")
     @TelephonyBaseTest.tel_test_wrap
@@ -426,15 +467,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at eSIM (slot 1)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            0,
-            1,
-            mt_rat=["volte", "5g_volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="33f2fa73-de7b-4b68-b9b8-aa08f6511e1a")
     @TelephonyBaseTest.tel_test_wrap
@@ -444,15 +486,16 @@
             - eSIM 5G NSA VoLTE
             - DDS at eSIM (slot 1)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            1,
-            None,
-            1,
-            mo_rat=["volte", "5g_volte"],
-            call_direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="b1ae55f1-dfd4-4e50-a0e3-df3b3ae29c68")
     @TelephonyBaseTest.tel_test_wrap
@@ -462,15 +505,473 @@
             - eSIM 5G NSA VoLTE
             - DDS at eSIM (slot 1)
         """
-        return dsds_voice_call_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
-            None,
-            1,
-            1,
-            mt_rat=["volte", "5g_volte"],
-            call_direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
+
+    # e+e
+    @test_tracker_info(uuid="9c35e485-b813-4af2-b30f-d331337a6eaf")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mo_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_1(self):
+        """A MO VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="d321af5a-ec10-4933-9fe4-ebbc2ac52756")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mt_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_1(self):
+        """A MT VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="ca53ccbb-bcc9-4c04-897e-ac95dfb8178d")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mo_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_1(self):
+        """A MO VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="0417e06b-2669-454c-b825-ac21a115a4b5")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mt_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_1(self):
+        """A MT VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="d61c930e-4fca-46f6-a946-753ddfbd0a46")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mo_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_2(self):
+        """A MO VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="c157cad9-7d04-40fb-9fe5-c9d691d6b7e9")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mt_5g_nsa_volte_esim_port_1_5g_nsa_volte_dds_2(self):
+        """A MT VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="acaa152c-4e0a-4caa-86a6-6689ff519e9e")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mo_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_2(self):
+        """A MO VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="d8f45a79-e86b-4292-96a0-fc32067b1d90")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mt_5g_nsa_volte_esim_port_0_5g_nsa_volte_dds_2(self):
+        """A MT VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "5g_volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="fa199595-9081-4a1b-b0ce-dcff190dc403")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mo_5g_nsa_volte_esim_port_1_4g_volte_dds_1(self):
+        """A MO VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="a6d3f4b4-085e-4ca4-94f7-d1bcccdaaa03")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mt_5g_nsa_volte_esim_port_1_4g_volte_dds_1(self):
+        """A MT VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="22c202e0-b488-4700-acbe-3c3f53f6d7f1")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mo_4g_volte_esim_port_0_5g_nsa_volte_dds_1(self):
+        """A MO VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="4a6de885-b092-42ae-9209-7b4d2407034c")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mt_4g_volte_esim_port_0_5g_nsa_volte_dds_1(self):
+        """A MT VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="003d9ffe-c057-4191-8289-3419de874c02")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mo_5g_nsa_volte_esim_port_1_4g_volte_dds_2(self):
+        """A MO VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="	c4fdc99c-1d6c-4466-b811-ca948a48b53c")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mt_5g_nsa_volte_esim_port_1_4g_volte_dds_2(self):
+        """A MT VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="95c6b512-2dbc-4435-a0ba-133d79ace3bc")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mo_4g_volte_esim_port_0_5g_nsa_volte_dds_2(self):
+        """A MO VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="a51d65d2-00e1-489c-8ede-132c4449638c")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mt_4g_volte_esim_port_0_5g_nsa_volte_dds_2(self):
+        """A MT VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["5g_volte", "volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="c0108b40-a7dd-482e-88c5-910996e0e35c")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mo_4g_volte_esim_port_1_5g_nsa_volte_dds_1(self):
+        """A MO VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="077db5cb-f41e-4ccd-a921-217b37eeab93")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mt_4g_volte_esim_port_1_5g_nsa_volte_dds_1(self):
+        """A MT VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="a6d910c7-bbf5-4cec-b0c3-e08f0c9ab9e8")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mo_5g_nsa_volte_esim_port_0_4g_volte_dds_1(self):
+        """A MO VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="25678592-49aa-4070-a889-4193ab02d575")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mt_5g_nsa_volte_esim_port_0_4g_volte_dds_1(self):
+        """A MT VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="610a4a93-1db5-4938-82b1-cdbcd0de9140")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mo_4g_volte_esim_port_1_5g_nsa_volte_dds_2(self):
+        """A MO VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="ac2d2dcd-cc0d-4500-b27b-3806a7f008dd")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_0_mt_4g_volte_esim_port_1_5g_nsa_volte_dds_2(self):
+        """A MT VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="b5dfe6dc-a9cd-46d6-a227-359cc3ac05cc")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mo_5g_nsa_volte_esim_port_0_4g_volte_dds_2(self):
+        """A MO VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="1449a187-d388-4b74-bd8f-d45827441604")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_esim_port_1_mt_5g_nsa_volte_esim_port_0_4g_volte_dds_2(self):
+        """A MT VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 5G NSA VoLTE
+            - eSIM port 1 5G NSA VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "5g_volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="f94d5fd2-79ac-426a-9a0d-1ba72e070b19")
     @TelephonyBaseTest.tel_test_wrap
@@ -2163,13 +2664,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mo",
             duration=360,
             streaming=False)
@@ -2185,13 +2687,13 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mt",
             duration=360,
             streaming=False)
@@ -2207,13 +2709,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mo",
             duration=360,
             streaming=False)
@@ -2229,13 +2732,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mt",
             duration=360,
             streaming=False)
@@ -2251,13 +2755,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "5g_volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mo",
             duration=360,
             streaming=False)
@@ -2273,13 +2778,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "5g_volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mt",
             duration=360,
             streaming=False)
@@ -2295,13 +2801,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mo",
             duration=360,
             streaming=False)
@@ -2317,13 +2824,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mt",
             duration=360,
             streaming=False)
@@ -2339,13 +2847,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mo",
             duration=360,
             streaming=False)
@@ -2361,13 +2870,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mt",
             duration=360,
             streaming=False)
@@ -2383,13 +2893,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "5g_volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mo",
             duration=360,
             streaming=False)
@@ -2405,13 +2916,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "5g_volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mt",
             duration=360,
             streaming=False)
@@ -2427,13 +2939,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mo",
             duration=360,
             streaming=False)
@@ -2449,13 +2962,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mt",
             duration=360,
             streaming=False)
@@ -2471,13 +2985,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mo",
             duration=360,
             streaming=False)
@@ -2493,13 +3008,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mt",
             duration=360,
             streaming=False)
@@ -2515,13 +3031,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mo",
             duration=360,
             streaming=True)
@@ -2537,13 +3054,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mt",
             duration=360,
             streaming=True)
@@ -2559,13 +3077,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mo",
             duration=360,
             streaming=True)
@@ -2581,13 +3100,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mt",
             duration=360,
             streaming=True)
@@ -2603,13 +3123,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "5g_volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mo",
             duration=360,
             streaming=True)
@@ -2625,13 +3146,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "5g_volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mt",
             duration=360,
             streaming=True)
@@ -2647,13 +3169,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mo",
             duration=360,
             streaming=True)
@@ -2669,13 +3192,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "5g_volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mt",
             duration=360,
             streaming=True)
@@ -2691,13 +3215,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mo",
             duration=360,
             streaming=True)
@@ -2713,13 +3238,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["5g_volte", "volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mt",
             duration=360,
             streaming=True)
@@ -2735,13 +3261,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "5g_volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mo",
             duration=360,
             streaming=True)
@@ -2757,13 +3284,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "5g_volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mt",
             duration=360,
             streaming=True)
@@ -2779,13 +3307,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mo",
             duration=360,
             streaming=True)
@@ -2801,13 +3330,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "volte"],
-            test_slot=1,
             dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
             direction="mt",
             duration=360,
             streaming=True)
@@ -2823,13 +3353,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mo",
             duration=360,
             streaming=True)
@@ -2845,13 +3376,14 @@
             After call end will check the eSIM if is attach to the network
             with assigned RAT successfully and data works fine.
         """
-        return dsds_long_call_streaming_test(
+        return dsds_call_streaming_test(
             self.log,
             self.tel_logger,
             self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
             test_rat=["volte", "volte"],
-            test_slot=0,
             dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
             direction="mt",
             duration=360,
             streaming=True)
\ No newline at end of file
diff --git a/acts_tests/tests/google/nr/nsa5g/Nsa5gMmsTest.py b/acts_tests/tests/google/nr/nsa5g/Nsa5gMmsTest.py
index e7bcb5a..baafb51 100755
--- a/acts_tests/tests/google/nr/nsa5g/Nsa5gMmsTest.py
+++ b/acts_tests/tests/google/nr/nsa5g/Nsa5gMmsTest.py
@@ -17,8 +17,6 @@
     Test Script for 5G MMS scenarios
 """
 
-import time
-
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
@@ -53,8 +51,8 @@
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
 
-
     """ Tests Begin """
+
     @test_tracker_info(uuid="bc484c2c-8086-42db-94cd-a1e4a35f35cf")
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_nsa_mms_mo_mt(self):
@@ -68,13 +66,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g',
-            mt_rat='5g',
-            msg_type='mms')
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g',
+                            mt_rat='5g',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="88bd6658-30fa-41b1-b5d9-0f9dadd83219")
     @TelephonyBaseTest.tel_test_wrap
@@ -89,13 +86,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g',
-            mt_rat='default',
-            msg_type='mms')
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g',
+                            mt_rat='default',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="11f2e2c8-bb63-43fa-b279-e7bb32f80596")
     @TelephonyBaseTest.tel_test_wrap
@@ -110,13 +106,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g',
-            msg_type='mms')
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="51d42104-cb87-4c9b-9a16-302e246a21dc")
     @TelephonyBaseTest.tel_test_wrap
@@ -132,13 +127,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_volte',
-            mt_rat='5g_volte',
-            msg_type='mms')
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_volte',
+                            mt_rat='5g_volte',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="97d6b071-aef2-40c1-8245-7be6c31870a6")
     @TelephonyBaseTest.tel_test_wrap
@@ -154,14 +148,13 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_volte',
-            mt_rat='5g_volte',
-            msg_type='mms',
-            msg_in_call=True)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_volte',
+                            mt_rat='5g_volte',
+                            msg_type='mms',
+                            msg_in_call=True)
 
     @test_tracker_info(uuid="bbb4b80c-fc1b-4377-b3c7-eeed642c5980")
     @TelephonyBaseTest.tel_test_wrap
@@ -177,18 +170,20 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='5g_wfc',
-            msg_type='mms',
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='5g_wfc',
+                            msg_type='mms',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="d36d95dc-0973-4711-bb08-c29ce23495e4")
     @TelephonyBaseTest.tel_test_wrap
@@ -204,17 +199,19 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='5g_wfc',
-            msg_type='mms',
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='5g_wfc',
+                            msg_type='mms',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="74ffb79e-f1e9-4087-a9d2-e07878e47869")
     @TelephonyBaseTest.tel_test_wrap
@@ -230,19 +227,21 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='5g_wfc',
-            msg_type='mms',
-            msg_in_call=True,
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='5g_wfc',
+                            msg_type='mms',
+                            msg_in_call=True,
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="68c8e0ca-bea4-45e4-92cf-19424ee47ca4")
     @TelephonyBaseTest.tel_test_wrap
@@ -258,16 +257,15 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_volte',
-            mt_rat='5g_volte',
-            msg_type='mms',
-            msg_in_call=True,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_volte',
+                            mt_rat='5g_volte',
+                            msg_type='mms',
+                            msg_in_call=True,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="8c795c3a-59d4-408c-9b99-5287e79ba00b")
     @TelephonyBaseTest.tel_test_wrap
@@ -282,14 +280,13 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g',
-            mt_rat='5g',
-            msg_type='mms',
-            long_msg=True)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g',
+                            mt_rat='5g',
+                            msg_type='mms',
+                            long_msg=True)
 
     @test_tracker_info(uuid="e09b82ab-69a9-4eae-8cbe-b6f2cff993ad")
     @TelephonyBaseTest.tel_test_wrap
@@ -305,15 +302,14 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g',
-            mt_rat='general',
-            msg_type='mms',
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g',
+                            mt_rat='general',
+                            msg_type='mms',
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="fedae24f-2577-4f84-9d76-53bbbe109d48")
     @TelephonyBaseTest.tel_test_wrap
@@ -329,15 +325,14 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='general',
-            mt_rat='5g',
-            msg_type='mms',
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='general',
+                            mt_rat='5g',
+                            msg_type='mms',
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="156bf832-acc2-4729-a69d-b471cd5cfbde")
     @TelephonyBaseTest.tel_test_wrap
@@ -355,16 +350,15 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_csfb',
-            mt_rat='5g_csfb',
-            msg_type='mms',
-            msg_in_call=True,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_csfb',
+                            mt_rat='5g_csfb',
+                            msg_type='mms',
+                            msg_in_call=True,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="a76e4adc-ce37-47d4-9925-4ebe175f7b9c")
     @TelephonyBaseTest.tel_test_wrap
@@ -380,13 +374,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_volte',
-            mt_rat='default',
-            msg_type='mms')
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_volte',
+                            mt_rat='default',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="c2282b01-e89f-49db-8925-79d38b63a373")
     @TelephonyBaseTest.tel_test_wrap
@@ -402,13 +395,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_volte',
-            msg_type='mms')
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_volte',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="fd9bc699-940f-4a4a-abf1-31080e54ab56")
     @TelephonyBaseTest.tel_test_wrap
@@ -424,14 +416,13 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_volte',
-            mt_rat='default',
-            msg_type='mms',
-            msg_in_call=True)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_volte',
+                            mt_rat='default',
+                            msg_type='mms',
+                            msg_in_call=True)
 
     @test_tracker_info(uuid="cfbae1e0-842a-470a-914a-a3a25a18dc81")
     @TelephonyBaseTest.tel_test_wrap
@@ -447,14 +438,13 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_volte',
-            msg_type='mms',
-            msg_in_call=True)
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_volte',
+                            msg_type='mms',
+                            msg_in_call=True)
 
     @test_tracker_info(uuid="fc8a996b-04b5-40e0-be25-cbbabf4d7957")
     @TelephonyBaseTest.tel_test_wrap
@@ -470,18 +460,20 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='default',
-            msg_type='mms',
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='default',
+                            msg_type='mms',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="7f354997-38b5-49cd-8bee-12d0589e0380")
     @TelephonyBaseTest.tel_test_wrap
@@ -497,18 +489,20 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_wfc',
-            msg_type='mms',
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_wfc',
+                            msg_type='mms',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="592ea897-cba1-4ab5-a4ed-54ac1f8d3039")
     @TelephonyBaseTest.tel_test_wrap
@@ -524,17 +518,19 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='default',
-            msg_type='mms',
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='default',
+                            msg_type='mms',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="3824205d-6a36-420f-a448-51ebb30948c2")
     @TelephonyBaseTest.tel_test_wrap
@@ -550,17 +546,19 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_wfc',
-            msg_type='mms',
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_wfc',
+                            msg_type='mms',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="91da5493-c810-4b1e-84f0-9d292a7b23eb")
     @TelephonyBaseTest.tel_test_wrap
@@ -576,19 +574,21 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='default',
-            msg_type='mms',
-            msg_in_call=True,
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='default',
+                            msg_type='mms',
+                            msg_in_call=True,
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="3e6a6700-1fcb-4db1-a757-e80801032605")
     @TelephonyBaseTest.tel_test_wrap
@@ -604,19 +604,21 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_wfc',
-            msg_type='mms',
-            msg_in_call=True,
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_wfc',
+                            msg_type='mms',
+                            msg_in_call=True,
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="dc483cc-d7c7-4cdd-9500-4bfc4f1b5bab")
     @TelephonyBaseTest.tel_test_wrap
@@ -632,16 +634,15 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_volte',
-            mt_rat='default',
-            msg_type='mms',
-            msg_in_call=True,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_volte',
+                            mt_rat='default',
+                            msg_type='mms',
+                            msg_in_call=True,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="95472ce7-0947-4199-bb6a-8fbb189f3c5c")
     @TelephonyBaseTest.tel_test_wrap
@@ -657,16 +658,15 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_volte',
-            msg_type='mms',
-            msg_in_call=True,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_volte',
+                            msg_type='mms',
+                            msg_in_call=True,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="738e2d29-c82d-4a4a-9f4b-e8f8688151ee")
     @TelephonyBaseTest.tel_test_wrap
@@ -681,14 +681,13 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g',
-            mt_rat='default',
-            msg_type='mms',
-            long_msg=True)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g',
+                            mt_rat='default',
+                            msg_type='mms',
+                            long_msg=True)
 
     @test_tracker_info(uuid="68f4f0d6-b798-4d0b-9500-ce49f009b61a")
     @TelephonyBaseTest.tel_test_wrap
@@ -703,14 +702,13 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g',
-            msg_type='mms',
-            long_msg=True)
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g',
+                            msg_type='mms',
+                            long_msg=True)
 
     @test_tracker_info(uuid="a379fac4-1aa6-46e0-8cef-6d2452702e04")
     @TelephonyBaseTest.tel_test_wrap
@@ -728,16 +726,15 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_csfb',
-            mt_rat='default',
-            msg_type='mms',
-            msg_in_call=True,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_csfb',
+                            mt_rat='default',
+                            msg_type='mms',
+                            msg_in_call=True,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="1a6543b1-b7d6-4260-8276-88aee649c4b2")
     @TelephonyBaseTest.tel_test_wrap
@@ -755,16 +752,15 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_csfb',
-            msg_type='mms',
-            msg_in_call=True,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_csfb',
+                            msg_type='mms',
+                            msg_in_call=True,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="536c8e25-2d72-46a6-89e1-03f70c5a28a3")
     @TelephonyBaseTest.tel_test_wrap
@@ -801,12 +797,13 @@
         if not active_file_download_task(self.log, cell_2):
             return False
         download_task = active_file_download_task(self.log, cell_2, "10MB")
-        message_task = (message_test, (self.log, cell_2, cell_1,
-                                        '5g', 'volte', 'mms'))
+        message_task = (message_test, (self.log, cell_2, cell_1, '5g', 'volte',
+                                       'mms'))
         results = run_multithread_func(self.log, [download_task, message_task])
 
         if ((results[0]) & (results[1])):
-            self.log.info("PASS - MO MMS test validated over active data transfer")
+            self.log.info(
+                "PASS - MO MMS test validated over active data transfer")
         elif ((results[0] == False) & (results[1] == True)):
             self.log.error("FAIL - Data Transfer failed")
         elif ((results[0] == True) & (results[1] == False)):
@@ -852,12 +849,13 @@
             return False
 
         download_task = active_file_download_task(self.log, cell_2, "10MB")
-        message_task = (message_test, (self.log, cell_1, cell_2,
-                                        'volte', '5g', 'mms'))
+        message_task = (message_test, (self.log, cell_1, cell_2, 'volte', '5g',
+                                       'mms'))
         results = run_multithread_func(self.log, [download_task, message_task])
 
         if ((results[0]) & (results[1])):
-            self.log.info("PASS - MT MMS test validated over active data transfer")
+            self.log.info(
+                "PASS - MT MMS test validated over active data transfer")
         elif ((results[0] == False) & (results[1] == True)):
             self.log.error("FAIL - Data Transfer failed")
         elif ((results[0] == True) & (results[1] == False)):
diff --git a/acts_tests/tests/google/nr/nsa5g/Nsa5gSettingsTest.py b/acts_tests/tests/google/nr/nsa5g/Nsa5gSettingsTest.py
index 1fdf362..5f5b180 100644
--- a/acts_tests/tests/google/nr/nsa5g/Nsa5gSettingsTest.py
+++ b/acts_tests/tests/google/nr/nsa5g/Nsa5gSettingsTest.py
@@ -13,25 +13,18 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-
 '''
     Test Script for Telephony Settings on nsa 5G
 '''
 
-import time
-
 from acts.test_decorators import test_tracker_info
-from acts_contrib.test_utils.net import ui_utils
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from acts_contrib.test_utils.tel.tel_defines import MOBILE_DATA
-from acts_contrib.test_utils.tel.tel_defines import USE_SIM
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
-from acts_contrib.test_utils.tel.tel_ops_utils import get_resource_value
-from acts_contrib.test_utils.tel.tel_ops_utils import wait_and_click_element
+from acts_contrib.test_utils.tel.tel_defines import GEN_5G
 from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
-from acts_contrib.test_utils.tel.tel_test_utils import get_current_override_network_type
-from acts_contrib.test_utils.tel.tel_5g_test_utils import provision_device_for_5g
-from acts_contrib.test_utils.tel.tel_5g_utils import is_current_network_5g
+from acts_contrib.test_utils.tel.tel_settings_utils import att_apn_test
+from acts_contrib.test_utils.tel.tel_settings_utils import tmo_apn_test
+from acts_contrib.test_utils.tel.tel_settings_utils import toggle_mobile_data_test
+from acts_contrib.test_utils.tel.tel_settings_utils import toggle_sim_test
 
 
 class Nsa5gSettingsTest(TelephonyBaseTest):
@@ -45,8 +38,8 @@
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
 
-
     """ Tests Begin """
+
     @test_tracker_info(uuid='57debc2d-ca17-4363-8d03-9bc068fdc624')
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_nsa_disable_enable_sim(self):
@@ -66,55 +59,7 @@
             True is tests passes else False
         """
         ad = self.android_devices[0]
-
-        if not provision_device_for_5g(ad.log, ad, nr_type='nsa'):
-            return False
-        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
-
-        ad.adb.shell('am start -a android.settings.WIRELESS_SETTINGS')
-        wait_and_click_element(ad, 'SIMs')
-
-        switch_value = get_resource_value(ad, USE_SIM)
-        if switch_value == 'true':
-            ad.log.info('SIM is enabled as expected')
-        else:
-            ad.log.error('SIM should be enabled but SIM is disabled')
-            return False
-
-        label_text = USE_SIM
-        label_resource_id = 'com.android.settings:id/switch_text'
-
-        ad.log.info('Disable SIM')
-        wait_and_click_element(ad, label_text, label_resource_id)
-
-        button_resource_id = 'android:id/button1'
-        wait_and_click_element(ad, 'Yes', button_resource_id)
-        switch_value = get_resource_value(ad, USE_SIM)
-        if switch_value == 'false':
-            ad.log.info('SIM is disabled as expected')
-        else:
-            ad.log.error('SIM should be disabled but SIM is enabled')
-            return False
-
-        ad.log.info('Enable SIM')
-        wait_and_click_element(ad, label_text, label_resource_id)
-
-        wait_and_click_element(ad, 'Yes', button_resource_id)
-        switch_value = get_resource_value(ad, USE_SIM)
-        if switch_value == 'true':
-            ad.log.info('SIM is enabled as expected')
-        else:
-            ad.log.error('SIM should be enabled but SIM is disabled')
-            return False
-
-        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
-
-        if is_current_network_5g(ad, nr_type = 'nsa', timeout=60):
-            ad.log.info('Success! attached on 5g NSA')
-        else:
-            ad.log.error('Failure - expected NR_NSA, current %s',
-                         get_current_override_network_type(ad))
-            return False
+        return toggle_sim_test(ad, GEN_5G, 'nsa')
 
     @test_tracker_info(uuid='7233780b-eabf-4bb6-ae96-3574d0cd4fa2')
     @TelephonyBaseTest.tel_test_wrap
@@ -136,40 +81,96 @@
         """
         ad = self.android_devices[0]
 
-        if not provision_device_for_5g(ad.log, ad, nr_type='nsa'):
-            return False
-        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+        return toggle_mobile_data_test(ad, GEN_5G, 'nsa')
 
-        ad.adb.shell('am start -a android.settings.WIRELESS_SETTINGS')
-        wait_and_click_element(ad, 'SIMs')
-        switch_value = get_resource_value(ad, MOBILE_DATA)
+    @test_tracker_info(uuid='42e45721-0052-4bcc-8a10-2686dfb86648')
+    @TelephonyBaseTest.tel_test_wrap
+    def test_5g_nsa_apn_settings_att_sms(self):
+        """Test ATT APN and SMS
 
-        if switch_value == 'true':
-            ad.log.info('Mobile data is enabled as expected')
-        else:
-            ad.log.error('Mobile data should be enabled but it is disabled')
+        Steps:
+            1. Provision device to nsa 5G
+            2. Launch Settings - Network & Internet
+            3. Click on SIMs
+            4. Click on Access Point Names
+            5. Add New APN
+            6. Save New APN
+            7. Switch APN to New APN
+            8. Check Network is connected to nsa 5G
+            9. Send SMS
 
-        ad.log.info('Disable mobile data')
-        ad.droid.telephonyToggleDataConnection(False)
-        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
-        switch_value = get_resource_value(ad, MOBILE_DATA)
-        if switch_value == 'false':
-            ad.log.info('Mobile data is disabled as expected')
-        else:
-            ad.log.error('Mobile data should be disabled but it is enabled')
+        Returns:
+            True is tests passes else False
+        """
+        caller, callee = self.android_devices[0], self.android_devices[1]
 
-        ad.log.info('Enabling mobile data')
-        ad.droid.telephonyToggleDataConnection(True)
-        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
-        switch_value = get_resource_value(ad, MOBILE_DATA)
-        if switch_value == 'true':
-            ad.log.info('Mobile data is enabled as expected')
-        else:
-            ad.log.error('Mobile data should be enabled but it is disabled')
+        return att_apn_test(self.log, caller, callee, GEN_5G, nr_type='nsa', msg_type='sms')
 
-        if is_current_network_5g(ad, nr_type = 'nsa', timeout=60):
-            ad.log.info('Success! attached on 5g NSA')
-        else:
-            ad.log.error('Failure - expected NR_NSA, current %s',
-                         get_current_override_network_type(ad))
+    @test_tracker_info(uuid='5a295807-c5cb-4a5a-ad25-e44d4a16cfe6')
+    @TelephonyBaseTest.tel_test_wrap
+    def test_5g_nsa_apn_settings_att_mms(self):
+        """Test ATT APN and MMS
 
+        Steps:
+            1. Provision device to nsa 5G
+            2. Launch Settings - Network & Internet
+            3. Click on SIMs
+            4. Click on Access Point Names
+            5. Add New APN
+            6. Add ATT APN details and Save
+            7. Switch APN to New APN
+            8. Check Network is connected to nsa 5G
+            9. Send MMS
+
+        Returns:
+            True is tests passes else False
+        """
+        caller, callee = self.android_devices[0], self.android_devices[1]
+
+        return att_apn_test(self.log, caller, callee, GEN_5G, nr_type='nsa', msg_type='mms')
+
+    @test_tracker_info(uuid='952b3861-0516-42e6-a221-23934bdab13c')
+    @TelephonyBaseTest.tel_test_wrap
+    def test_5g_nsa_apn_settings_tmo_sms(self):
+        """Test TMO APN and SMS
+
+        Steps:
+            1. Provision device to nsa 5G
+            2. Launch Settings - Network & Internet
+            3. Click on SIMs
+            4. Click on Access Point Names
+            5. Add New APN
+            6. Save New APN
+            7. Switch APN to New APN
+            8. Check Network is connected to nsa 5G
+            9. Send SMS
+
+        Returns:
+            True is tests passes else False
+        """
+        caller, callee = self.android_devices[0], self.android_devices[1]
+
+        return tmo_apn_test(self.log, caller, callee, GEN_5G, nr_type='nsa', msg_type='sms')
+
+    @test_tracker_info(uuid='62d66d9e-b7de-419b-b079-9d0112cb28dc')
+    @TelephonyBaseTest.tel_test_wrap
+    def test_5g_nsa_apn_settings_tmo_mms(self):
+        """Test TMO APN and MMS
+
+        Steps:
+            1. Provision device to nsa 5G
+            2. Launch Settings - Network & Internet
+            3. Click on SIMs
+            4. Click on Access Point Names
+            5. Add New APN
+            6. Add ATT APN details and Save
+            7. Switch APN to New APN
+            8. Check Network is connected to nsa 5G
+            9. Send MMS
+
+        Returns:
+            True is tests passes else False
+        """
+        caller, callee = self.android_devices[0], self.android_devices[1]
+
+        return tmo_apn_test(self.log, caller, callee, GEN_5G, nr_type='nsa', msg_type='mms')
diff --git a/acts_tests/tests/google/nr/nsa5g/Nsa5gSmsTest.py b/acts_tests/tests/google/nr/nsa5g/Nsa5gSmsTest.py
index f1df4f4..88f1fcd 100755
--- a/acts_tests/tests/google/nr/nsa5g/Nsa5gSmsTest.py
+++ b/acts_tests/tests/google/nr/nsa5g/Nsa5gSmsTest.py
@@ -17,8 +17,6 @@
     Test Script for 5G SMS scenarios
 """
 
-import time
-
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
@@ -53,8 +51,8 @@
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
 
-
     """ Tests Begin """
+
     @test_tracker_info(uuid="4a64a262-7433-4a7f-b5c6-a36ff60aeaa2")
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_nsa_sms_mo_mt(self):
@@ -68,12 +66,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g',
-            mt_rat='5g')
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g',
+                            mt_rat='5g')
 
     @test_tracker_info(uuid="52b16764-0c9e-45c0-910f-a39d17c7cf7e")
     @TelephonyBaseTest.tel_test_wrap
@@ -88,12 +85,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g',
-            mt_rat='default')
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g',
+                            mt_rat='default')
 
     @test_tracker_info(uuid="e9b2494a-0e40-449c-b877-1e4ddc78c536")
     @TelephonyBaseTest.tel_test_wrap
@@ -108,12 +104,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g')
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g')
 
     @test_tracker_info(uuid="2ce809d4-cbf6-4233-81ad-43f91107b201")
     @TelephonyBaseTest.tel_test_wrap
@@ -129,12 +124,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_volte',
-            mt_rat='5g_volte')
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_volte',
+                            mt_rat='5g_volte')
 
     @test_tracker_info(uuid="e51f3dbb-bb16-4400-b2be-f9422f511087")
     @TelephonyBaseTest.tel_test_wrap
@@ -150,12 +144,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_volte',
-            mt_rat='default')
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_volte',
+                            mt_rat='default')
 
     @test_tracker_info(uuid="5217d427-04a2-4b2b-9ed8-28951e71fc21")
     @TelephonyBaseTest.tel_test_wrap
@@ -171,12 +164,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_volte')
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_volte')
 
     @test_tracker_info(uuid="49bfb4b3-a6ec-45d4-ad96-09282fb07d1d")
     @TelephonyBaseTest.tel_test_wrap
@@ -192,13 +184,12 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_volte',
-            mt_rat='5g_volte',
-            msg_in_call=True)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_volte',
+                            mt_rat='5g_volte',
+                            msg_in_call=True)
 
     @test_tracker_info(uuid="3d5c8f60-1eaa-4f4a-b539-c529fa36db91")
     @TelephonyBaseTest.tel_test_wrap
@@ -214,13 +205,12 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_volte',
-            mt_rat='default',
-            msg_in_call=True)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_volte',
+                            mt_rat='default',
+                            msg_in_call=True)
 
     @test_tracker_info(uuid="c71813f3-bb04-4115-8519-e23046349689")
     @TelephonyBaseTest.tel_test_wrap
@@ -236,13 +226,12 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_volte',
-            msg_in_call=True)
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_volte',
+                            msg_in_call=True)
 
     @test_tracker_info(uuid="1f914d5c-ac24-4794-9fcb-cb28e483d69a")
     @TelephonyBaseTest.tel_test_wrap
@@ -258,17 +247,19 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='5g_wfc',
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='5g_wfc',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="2d375f20-a785-42e0-b5a1-968d19bc693d")
     @TelephonyBaseTest.tel_test_wrap
@@ -284,17 +275,19 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='general',
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='general',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="db8b2b5b-bf9e-4a99-9fdb-dbd028567705")
     @TelephonyBaseTest.tel_test_wrap
@@ -310,17 +303,19 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='general',
-            mt_rat='5g_wfc',
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='general',
+                            mt_rat='5g_wfc',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="7274be32-b9dd-4ce3-83d1-f32ab14ce05e")
     @TelephonyBaseTest.tel_test_wrap
@@ -336,16 +331,18 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='5g_wfc',
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='5g_wfc',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="5997a618-efee-478f-8fa9-6cf8ba9cfc58")
     @TelephonyBaseTest.tel_test_wrap
@@ -362,16 +359,18 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='general',
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='general',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="352ca023-2cd1-4b08-877c-20c5d50cc265")
     @TelephonyBaseTest.tel_test_wrap
@@ -388,16 +387,18 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='general',
-            mt_rat='5g_wfc',
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='general',
+                            mt_rat='5g_wfc',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="2d1787f2-d6fe-4b41-b389-2a8f817594e4")
     @TelephonyBaseTest.tel_test_wrap
@@ -413,18 +414,20 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='5g_wfc',
-            msg_in_call=True,
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='5g_wfc',
+                            msg_in_call=True,
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="784062e8-02a4-49ce-8fc1-5359ab40bbdd")
     @TelephonyBaseTest.tel_test_wrap
@@ -439,13 +442,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g',
-            mt_rat='5g',
-            long_msg=True)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g',
+                            mt_rat='5g',
+                            long_msg=True)
 
     @test_tracker_info(uuid="45dbd61a-6a90-473e-9cfa-03e2408d5f15")
     @TelephonyBaseTest.tel_test_wrap
@@ -462,13 +464,12 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_csfb',
-            mt_rat='5g_csfb',
-            msg_in_call=True)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_csfb',
+                            mt_rat='5g_csfb',
+                            msg_in_call=True)
 
     @test_tracker_info(uuid="709d5322-3da3-4c77-9180-281bc54ad78e")
     @TelephonyBaseTest.tel_test_wrap
@@ -485,18 +486,20 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_wfc',
-            mt_rat='default',
-            msg_in_call=True,
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_wfc',
+                            mt_rat='default',
+                            msg_in_call=True,
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="6af38572-bbf7-4c11-8f0c-ab2f9b25ac49")
     @TelephonyBaseTest.tel_test_wrap
@@ -513,18 +516,20 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_wfc',
-            msg_in_call=True,
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_wfc',
+                            msg_in_call=True,
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="1437adb8-dfb0-49fb-8ecc-b456f60d7f64")
     @TelephonyBaseTest.tel_test_wrap
@@ -540,13 +545,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g',
-            mt_rat='default',
-            long_msg=True)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g',
+                            mt_rat='default',
+                            long_msg=True)
 
     @test_tracker_info(uuid="d34a4840-d1fa-46f1-885b-f67456225f50")
     @TelephonyBaseTest.tel_test_wrap
@@ -562,13 +566,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g',
-            long_msg=True)
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g',
+                            long_msg=True)
 
     @test_tracker_info(uuid="84e40f15-1d02-44b0-8103-f25f73dae7a1")
     @TelephonyBaseTest.tel_test_wrap
@@ -585,13 +588,12 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_csfb',
-            mt_rat='default',
-            msg_in_call=True)
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_csfb',
+                            mt_rat='default',
+                            msg_in_call=True)
 
     @test_tracker_info(uuid="259ccd94-2d70-450e-adf4-949889096cce")
     @TelephonyBaseTest.tel_test_wrap
@@ -608,13 +610,12 @@
         Returns:
             True if pass; False if fail.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_csfb',
-            msg_in_call=True)
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_csfb',
+                            msg_in_call=True)
 
     @test_tracker_info(uuid="303d5c2f-15bd-4608-96b8-37d16341004e")
     @TelephonyBaseTest.tel_test_wrap
@@ -651,12 +652,13 @@
         if not active_file_download_task(self.log, cell_2):
             return False
         download_task = active_file_download_task(self.log, cell_2, "10MB")
-        message_task = (message_test, (self.log, cell_2, cell_1,
-                                        '5g', 'volte', 'sms'))
+        message_task = (message_test, (self.log, cell_2, cell_1, '5g', 'volte',
+                                       'sms'))
         results = run_multithread_func(self.log, [download_task, message_task])
 
         if ((results[0]) & (results[1])):
-            self.log.info("PASS - MO SMS test validated over active data transfer")
+            self.log.info(
+                "PASS - MO SMS test validated over active data transfer")
         elif ((results[0] == False) & (results[1] == True)):
             self.log.error("FAIL - Data Transfer failed")
         elif ((results[0] == True) & (results[1] == False)):
@@ -702,12 +704,13 @@
             return False
 
         download_task = active_file_download_task(self.log, cell_2, "10MB")
-        message_task = (message_test, (self.log, cell_1, cell_2,
-                                        'volte', '5g', 'sms'))
+        message_task = (message_test, (self.log, cell_1, cell_2, 'volte', '5g',
+                                       'sms'))
         results = run_multithread_func(self.log, [download_task, message_task])
 
         if ((results[0]) & (results[1])):
-            self.log.info("PASS - MT SMS test validated over active data transfer")
+            self.log.info(
+                "PASS - MT SMS test validated over active data transfer")
         elif ((results[0] == False) & (results[1] == True)):
             self.log.error("FAIL - Data Transfer failed")
         elif ((results[0] == True) & (results[1] == False)):
diff --git a/acts_tests/tests/google/nr/nsa5g/Nsa5gVoiceConfTest.py b/acts_tests/tests/google/nr/nsa5g/Nsa5gVoiceConfTest.py
index b4bbbd0..99006c7 100644
--- a/acts_tests/tests/google/nr/nsa5g/Nsa5gVoiceConfTest.py
+++ b/acts_tests/tests/google/nr/nsa5g/Nsa5gVoiceConfTest.py
@@ -13,13 +13,10 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-
 """
     Test Script for 5G Voice Conference scenarios
 """
 
-
-import time
 from acts import signals
 from acts.test_decorators import test_tracker_info
 from acts.libs.utils.multithread import multithread_func
@@ -54,9 +51,8 @@
     def setup_class(self):
         TelephonyBaseTest.setup_class(self)
         if not get_capability_for_subscription(
-            self.android_devices[0],
-            CAPABILITY_CONFERENCE,
-            get_outgoing_voice_sub_id(self.android_devices[0])):
+                self.android_devices[0], CAPABILITY_CONFERENCE,
+                get_outgoing_voice_sub_id(self.android_devices[0])):
             self.android_devices[0].log.error(
                 "Conference call is not supported, abort test.")
             raise signals.TestAbortClass(
@@ -74,10 +70,8 @@
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
 
-
     """ Tests Begin """
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="68a8be5b-a5e0-4475-a5bd-42adfdb6535f")
     def test_5g_nsa_volte_mo_mo_add_5g_nsa_volte_merge_drop_second_call_from_participant(
@@ -94,25 +88,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="bd828db0-dcc5-4d49-b049-48dd76f5f2f0")
-    def test_5g_nsa_volte_mo_mo_add_5g_nsa_volte_merge_drop_second_call_from_host(self):
+    def test_5g_nsa_volte_mo_mo_add_5g_nsa_volte_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5G VoLTE) to PhoneB (nsa 5G VoLTE), accept on PhoneB.
@@ -125,21 +120,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                       ads=ads,
-                                       num_swaps=0,
-                                       phone_setup_a=provision_device_for_5g,
-                                       phone_setup_b=provision_device_for_5g,
-                                       phone_setup_c=provision_device_for_5g,
-                                       verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                       verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                       verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="5c3a3370-094d-43ff-b024-58a8a2983274")
@@ -157,25 +152,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="9ae61738-925b-44f8-b3a7-0e58f2ebd6ec")
-    def test_5g_nsa_volte_mo_mo_add_5g_nsa_volte_merge_drop_first_call_from_host(self):
+    def test_5g_nsa_volte_mo_mo_add_5g_nsa_volte_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5G VoLTE) to PhoneB (nsa 5G VoLTE), accept on PhoneB.
@@ -188,25 +184,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="e3488699-9eb1-4a66-a2bd-b229c51e63c9")
-    def test_5g_nsa_volte_mo_mt_add_5g_nsa_volte_merge_drop_second_call_from_participant(self):
+    def test_5g_nsa_volte_mo_mt_add_5g_nsa_volte_merge_drop_second_call_from_participant(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5G VoLTE) to PhoneB (nsa 5G VoLTE), accept on PhoneB.
@@ -219,25 +216,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="15b23934-ac8c-4c3a-9967-c19e46c0d86b")
-    def test_5g_nsa_volte_mo_mt_add_5g_nsa_volte_merge_drop_second_call_from_host(self):
+    def test_5g_nsa_volte_mo_mt_add_5g_nsa_volte_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5G VoLTE) to PhoneB (nsa 5G VoLTE), accept on PhoneB.
@@ -250,21 +248,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="2d0b60db-44cd-48c2-8361-6a3af051eb22")
@@ -282,25 +280,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="c6b47773-70f2-413c-8f6c-29dc4abce44f")
-    def test_5g_nsa_volte_mo_mt_add_5g_nsa_volte_merge_drop_first_call_from_host(self):
+    def test_5g_nsa_volte_mo_mt_add_5g_nsa_volte_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5G VoLTE) to PhoneB (nsa 5G VoLTE), accept on PhoneB.
@@ -313,21 +312,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="47fe393b-9605-4c52-b322-c3baeb6aae3e")
@@ -345,25 +344,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="7d633e59-2918-4e7c-b01d-576bc2f31ab3")
-    def test_5g_nsa_volte_mt_mt_add_5g_nsa_volte_merge_drop_second_call_from_host(self):
+    def test_5g_nsa_volte_mt_mt_add_5g_nsa_volte_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneB (nsa 5G VoLTE) to PhoneA (nsa 5G VoLTE), accept on PhoneA.
@@ -376,21 +376,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="23faa690-e96f-40bc-ab2c-7c4e51f1323c")
@@ -408,25 +408,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="67dff58b-8111-41ba-9b8e-f06056b0db0a")
-    def test_5g_nsa_volte_mt_mt_add_5g_nsa_volte_merge_drop_first_call_from_host(self):
+    def test_5g_nsa_volte_mt_mt_add_5g_nsa_volte_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneB (nsa 5G VoLTE) to PhoneA (nsa 5G VoLTE), accept on PhoneA.
@@ -439,21 +440,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="4cf986be-d63c-472e-9702-9a30bf0d4f79")
@@ -471,25 +472,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="d77bd012-390d-433a-a705-b0a4d263d73c")
-    def test_5g_nsa_volte_mo_mo_add_4g_volte_merge_drop_second_call_from_host(self):
+    def test_5g_nsa_volte_mo_mo_add_4g_volte_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5G VoLTE) to PhoneB (VoLTE), accept on PhoneB.
@@ -502,21 +504,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="3d3945bc-39c3-42dc-9deb-a3f7fb46bba9")
@@ -534,25 +536,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="6bce0033-da88-4601-bc2f-7fd930b4cc6f")
-    def test_5g_nsa_volte_mo_mo_add_4g_volte_merge_drop_first_call_from_host(self):
+    def test_5g_nsa_volte_mo_mo_add_4g_volte_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5G VoLTE) to PhoneB (VoLTE), accept on PhoneB.
@@ -565,25 +568,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="c76be000-eda1-452e-a813-5f2b3c1ea85d")
-    def test_5g_nsa_volte_mo_mt_add_4g_volte_merge_drop_second_call_from_participant(self):
+    def test_5g_nsa_volte_mo_mt_add_4g_volte_merge_drop_second_call_from_participant(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5G VoLTE) to PhoneB (VoLTE), accept on PhoneB.
@@ -596,25 +600,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="ea104940-872a-4cf7-a47c-db620762c818")
-    def test_5g_nsa_volte_mo_mt_add_4g_volte_merge_drop_second_call_from_host(self):
+    def test_5g_nsa_volte_mo_mt_add_4g_volte_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5G VoLTE) to PhoneB (VoLTE), accept on PhoneB.
@@ -627,21 +632,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="1dde3ce7-cd02-4434-99b9-af025b5ada05")
@@ -659,25 +664,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="3ae5aee3-94c2-40ad-b8a1-c3e05a6c8365")
-    def test_5g_nsa_volte_mo_mt_add_4g_volte_merge_drop_first_call_from_host(self):
+    def test_5g_nsa_volte_mo_mt_add_4g_volte_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5G VoLTE) to PhoneB (VoLTE), accept on PhoneB.
@@ -690,21 +696,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="f45fb3c8-40b5-4731-9743-c6c63460ff9e")
@@ -722,25 +728,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="1ae31dcf-b5e5-44f1-aec7-472fa6300c16")
-    def test_5g_nsa_volte_mt_mt_add_4g_volte_merge_drop_second_call_from_host(self):
+    def test_5g_nsa_volte_mt_mt_add_4g_volte_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneB (VoLTE) to PhoneA (nsa 5G VoLTE), accept on PhoneA.
@@ -753,21 +760,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="b35ec011-e98c-4b79-98ea-7d6acf63c3fe")
@@ -785,25 +792,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="22db778c-bedb-4099-8764-f2832d87dcd9")
-    def test_5g_nsa_volte_mt_mt_add_4g_volte_merge_drop_first_call_from_host(self):
+    def test_5g_nsa_volte_mt_mt_add_4g_volte_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneB (VoLTE) to PhoneA (nsa 5G VoLTE), accept on PhoneA.
@@ -816,21 +824,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="e98ca225-7c65-4c61-bde7-9c3ef350dc30")
@@ -848,25 +856,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="3d7cf9ce-7658-46f7-a852-c429cd772bda")
-    def test_4g_volte_mo_mo_add_5g_nsa_volte_merge_drop_second_call_from_host(self):
+    def test_4g_volte_mo_mo_add_5g_nsa_volte_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (VoLTE) to PhoneB (nsa 5G VoLTE), accept on PhoneB.
@@ -879,21 +888,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="5803ba4f-4b73-4f88-a9c4-1b61a4afd84b")
@@ -911,25 +920,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="1f44800c-2bb2-46a3-9011-818f00e75e3f")
-    def test_4g_volte_mo_mo_add_5g_nsa_volte_merge_drop_first_call_from_host(self):
+    def test_4g_volte_mo_mo_add_5g_nsa_volte_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (VoLTE) to PhoneB (nsa 5G VoLTE), accept on PhoneB.
@@ -942,25 +952,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="fbc13772-314d-4b9a-8940-6edfe577c9f4")
-    def test_4g_volte_mo_mt_add_5g_nsa_volte_merge_drop_second_call_from_participant(self):
+    def test_4g_volte_mo_mt_add_5g_nsa_volte_merge_drop_second_call_from_participant(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (VoLTE) to PhoneB (nsa 5G VoLTE), accept on PhoneB.
@@ -973,25 +984,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="7b4e7c11-d633-4dc5-8fb1-25ad1ef4a4e8")
-    def test_4g_volte_mo_mt_add_5g_nsa_volte_merge_drop_second_call_from_host(self):
+    def test_4g_volte_mo_mt_add_5g_nsa_volte_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (VoLTE) to PhoneB (nsa 5G VoLTE), accept on PhoneB.
@@ -1004,21 +1016,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="98adc3f4-ab57-4485-946f-08c1e8a69efb")
@@ -1036,25 +1048,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="93adad3d-c3ca-4fc1-88ec-e32c5330efd6")
-    def test_4g_volte_mo_mt_add_5g_nsa_volte_merge_drop_first_call_from_host(self):
+    def test_4g_volte_mo_mt_add_5g_nsa_volte_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (VoLTE) to PhoneB (nsa 5G VoLTE), accept on PhoneB.
@@ -1067,21 +1080,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="6064eed6-0b23-4089-af30-f0c1f3ea844e")
@@ -1099,25 +1112,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="845e0521-fcb9-4609-a325-3ecb7681e3d8")
-    def test_4g_volte_mt_mt_add_5g_nsa_volte_merge_drop_second_call_from_host(self):
+    def test_4g_volte_mt_mt_add_5g_nsa_volte_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneB (nsa 5G VoLTE) to PhoneA (VoLTE), accept on PhoneA.
@@ -1130,21 +1144,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="c676851a-e723-420f-be64-b4dbcab9cbea")
@@ -1162,25 +1176,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="a2aac8d9-5f87-4d95-a4d3-d8b174652a0e")
-    def test_4g_volte_mt_mt_add_5g_nsa_volte_merge_drop_first_call_from_host(self):
+    def test_4g_volte_mt_mt_add_5g_nsa_volte_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneB (nsa 5G VoLTE) to PhoneA (VoLTE), accept on PhoneA.
@@ -1193,21 +1208,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="9c1a213f-8c85-4234-a974-2add9f698389")
@@ -1225,25 +1240,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="53882070-35d3-4b8b-bc09-4debce8a094c")
-    def test_5g_nsa_volte_mo_mo_add_wcdma_merge_drop_second_call_from_host(self):
+    def test_5g_nsa_volte_mo_mo_add_wcdma_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5g VoLTE) to PhoneB (WCDMA), accept on PhoneB.
@@ -1256,21 +1272,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="a69588a5-3e08-4d33-92f7-5397c4d1c58d")
@@ -1288,25 +1304,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="da541f3e-6054-4f92-bbe4-242d03d90da2")
-    def test_5g_nsa_volte_mo_mo_add_wcdma_merge_drop_first_call_from_host(self):
+    def test_5g_nsa_volte_mo_mo_add_wcdma_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5g VoLTE) to PhoneB (WCDMA), accept on PhoneB.
@@ -1319,21 +1336,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="54269ba5-bb78-428e-afd0-cba5783d797a")
@@ -1351,26 +1368,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="d2bf3176-f9e8-4d2a-ad2a-c49fed9fd913")
-    def test_5g_nsa_volte_mo_mt_add_wcdma_merge_drop_second_call_from_host(self):
+    def test_5g_nsa_volte_mo_mt_add_wcdma_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5g VoLTE) to PhoneB (WCDMA), accept on PhoneB.
@@ -1383,21 +1400,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="93c54a43-6b64-404a-9306-b9a74b563e41")
@@ -1415,25 +1432,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="8e699115-1513-4c86-acd4-099b05acb859")
-    def test_5g_nsa_volte_mo_mt_add_wcdma_merge_drop_first_call_from_host(self):
+    def test_5g_nsa_volte_mo_mt_add_wcdma_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneA (nsa 5g VoLTE) to PhoneB (WCDMA), accept on PhoneB.
@@ -1446,21 +1464,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="48edf043-1f45-4490-85fc-36ec49328768")
@@ -1478,25 +1496,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="a276b9d4-ddef-4712-9f96-aa4b71c7b35d")
-    def test_5g_nsa_volte_mt_mt_add_wcdma_merge_drop_second_call_from_host(self):
+    def test_5g_nsa_volte_mt_mt_add_wcdma_merge_drop_second_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneB (WCDMA) to PhoneA (nsa 5g VoLTE), accept on PhoneA.
@@ -1509,21 +1528,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_second_call_from_host(self.log, self.android_devices,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_second_call_from_host(
+            self.log, self.android_devices, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="d702a65e-c2c9-44cf-be13-16ae56377647")
@@ -1541,25 +1560,26 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_participant(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_participant(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="dd3df9eb-a600-4397-b257-b8465592af82")
-    def test_5g_nsa_volte_mt_mt_add_wcdma_merge_drop_first_call_from_host(self):
+    def test_5g_nsa_volte_mt_mt_add_wcdma_merge_drop_first_call_from_host(
+            self):
         """ Test nsa 5g VoLTE Conference Call among three phones.
 
         1. Call from PhoneB (WCDMA) to PhoneA (nsa 5g VoLTE), accept on PhoneA.
@@ -1572,21 +1592,21 @@
             True if pass; False if fail.
         """
         ads = self.android_devices
-        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=0,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_voice_3g,
-                                        phone_setup_c=phone_setup_voice_3g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_wcdma,
-                                        verify_phone_c_network_subscription=is_phone_in_call_wcdma)
+        call_ab_id, call_ac_id = _test_call_mt_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=0,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_voice_3g,
+            phone_setup_c=phone_setup_voice_3g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_wcdma,
+            verify_phone_c_network_subscription=is_phone_in_call_wcdma)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _test_ims_conference_merge_drop_first_call_from_host(self.log, ads,
-            call_ab_id, call_ac_id)
-
+        return _test_ims_conference_merge_drop_first_call_from_host(
+            self.log, ads, call_ab_id, call_ac_id)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="d1174d8f-80e4-4adf-8866-e00ac7706849")
@@ -1605,26 +1625,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="c631b985-1fc4-4de6-95cd-00258283ef62")
     def test_5g_nsa_volte_mo_mo_add_5g_nsa_volte_swap_twice_drop_active(self):
@@ -1641,26 +1662,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="0af58d3e-a7cc-4e5b-89df-b002deee022a")
     def test_5g_nsa_volte_mo_mt_add_5g_nsa_volte_swap_twice_drop_held(self):
@@ -1677,26 +1699,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="d00d0b87-4df2-40af-9b5e-dd425ec5ca4f")
     def test_5g_nsa_volte_mo_mt_add_5g_nsa_volte_swap_twice_drop_active(self):
@@ -1713,26 +1736,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="dc46308c-197f-4372-afca-b2ec131dcc34")
     def test_5g_nsa_volte_mo_mo_add_5g_nsa_volte_swap_once_drop_held(self):
@@ -1748,26 +1772,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="471cc6d1-03aa-41a0-b13a-fdd696f17fca")
     def test_5g_nsa_volte_mo_mo_add_5g_nsa_volte_swap_once_drop_active(self):
@@ -1783,26 +1808,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="bb9d69eb-40f4-494d-9399-7dbd046962e3")
     def test_5g_nsa_volte_mo_mt_add_5g_nsa_volte_swap_once_drop_held(self):
@@ -1818,25 +1844,26 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="33c9e964-c9d4-4cbc-894f-8682122598a7")
     def test_5g_nsa_volte_mo_mt_add_5g_nsa_volte_swap_once_drop_active(self):
@@ -1852,26 +1879,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="43e4cc02-93d9-4540-97b6-f5656d7d669f")
     def test_5g_nsa_volte_mo_mo_add_4g_volte_swap_twice_drop_held(self):
@@ -1888,26 +1916,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="f3081bbd-9f18-4c0b-a1ab-630d5e622259")
     def test_5g_nsa_volte_mo_mo_add_4g_volte_swap_twice_drop_active(self):
@@ -1924,26 +1953,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="ff4a731b-3bf9-45d5-beeb-e8ad53ceb722")
     def test_5g_nsa_volte_mo_mt_add_4g_volte_swap_twice_drop_held(self):
@@ -1960,26 +1990,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="9ebae664-41bb-45fe-8f52-08c2dabab659")
     def test_5g_nsa_volte_mo_mt_add_4g_volte_swap_twice_drop_active(self):
@@ -1996,26 +2027,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="b8ae81dd-3f67-4c2c-aec0-d6368efb872e")
     def test_5g_nsa_volte_mo_mo_add_4g_volte_swap_once_drop_held(self):
@@ -2031,26 +2063,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="2c588e87-8b69-4a8e-b55a-4e101806297c")
     def test_5g_nsa_volte_mo_mo_add_4g_volte_swap_once_drop_active(self):
@@ -2066,26 +2099,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="367e8d25-5a9d-4c0d-914d-c241399bb343")
     def test_5g_nsa_volte_mo_mt_add_4g_volte_swap_once_drop_held(self):
@@ -2101,25 +2135,26 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="8a8f915e-025a-4992-b05d-bf74173e3852")
     def test_5g_nsa_volte_mo_mt_add_4g_volte_swap_once_drop_active(self):
@@ -2135,26 +2170,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=provision_device_for_5g,
-                                        phone_setup_b=phone_setup_volte,
-                                        phone_setup_c=phone_setup_volte,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=provision_device_for_5g,
+            phone_setup_b=phone_setup_volte,
+            phone_setup_c=phone_setup_volte,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="880c3f9e-1a97-489a-80b1-db4c9aaa7be0")
     def test_4g_volte_mo_mo_add_5g_nsa_volte_swap_twice_drop_held(self):
@@ -2171,26 +2207,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="b869f455-bf1b-4f03-bba3-a6f029f9f594")
     def test_4g_volte_mo_mo_add_5g_nsa_volte_swap_twice_drop_active(self):
@@ -2207,26 +2244,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="32ce220a-7abe-40f4-bd59-e948e7e8f735")
     def test_4g_volte_mo_mt_add_5g_nsa_volte_swap_twice_drop_held(self):
@@ -2243,26 +2281,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="0b6bc57b-4dfb-4d2a-9160-fbebcecd8970")
     def test_4g_volte_mo_mt_add_5g_nsa_volte_swap_twice_drop_active(self):
@@ -2279,26 +2318,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=2,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=2,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="1d70289e-bbef-420a-b484-77c5573cc8fb")
     def test_4g_volte_mo_mo_add_5g_nsa_volte_swap_once_drop_held(self):
@@ -2314,26 +2354,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="3a62d358-ccb6-43fc-acb5-469dc9c5d5b2")
     def test_4g_volte_mo_mo_add_5g_nsa_volte_swap_once_drop_active(self):
@@ -2350,26 +2391,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mo_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="52e0a840-3fb3-4351-b2f0-d67cf9928a16")
     def test_4g_volte_mo_mt_add_5g_nsa_volte_swap_once_drop_held(self):
@@ -2385,25 +2427,26 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[2],
             ad_verify=ads[0],
             call_id=call_ab_id,
             call_state=CALL_STATE_ACTIVE,
             ads_active=[ads[0], ads[1]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="6b63555a-c009-4431-be14-abc546cff911")
     def test_4g_volte_mo_mt_add_5g_nsa_volte_swap_once_drop_active(self):
@@ -2419,26 +2462,27 @@
         """
         ads = self.android_devices
 
-        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(log=self.log,
-                                        ads=ads,
-                                        num_swaps=1,
-                                        phone_setup_a=phone_setup_volte,
-                                        phone_setup_b=provision_device_for_5g,
-                                        phone_setup_c=provision_device_for_5g,
-                                        verify_phone_a_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_b_network_subscription=is_phone_in_call_volte,
-                                        verify_phone_c_network_subscription=is_phone_in_call_volte)
+        call_ab_id, call_ac_id = _test_call_mo_mt_add_swap_x(
+            log=self.log,
+            ads=ads,
+            num_swaps=1,
+            phone_setup_a=phone_setup_volte,
+            phone_setup_b=provision_device_for_5g,
+            phone_setup_c=provision_device_for_5g,
+            verify_phone_a_network_subscription=is_phone_in_call_volte,
+            verify_phone_b_network_subscription=is_phone_in_call_volte,
+            verify_phone_c_network_subscription=is_phone_in_call_volte)
         if call_ab_id is None or call_ac_id is None:
             return False
 
-        return _three_phone_hangup_call_verify_call_state(log=self.log,
+        return _three_phone_hangup_call_verify_call_state(
+            log=self.log,
             ad_hangup=ads[1],
             ad_verify=ads[0],
             call_id=call_ac_id,
             call_state=_get_expected_call_state(ads[0]),
             ads_active=[ads[0], ads[2]])
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="f4990e20-4a40-4238-9a2a-a75d9be3d354")
     def test_5g_nsa_volte_call_forwarding_unconditional(self):
@@ -2461,7 +2505,6 @@
             ads[2],
             call_forwarding_type="unconditional")
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="26b85c3f-5a38-465a-a6e3-dfd03c6ea315")
     def test_5g_nsa_volte_call_forwarding_busy(self):
@@ -2484,7 +2527,6 @@
             ads[2],
             call_forwarding_type="busy")
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="96638a39-efe2-40e2-afb6-6a97f87c4af5")
     def test_5g_nsa_volte_call_forwarding_not_answered(self):
@@ -2507,7 +2549,6 @@
             ads[2],
             call_forwarding_type="not_answered")
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="a13e586a-3345-49d8-9e84-ca33bd3fbd7d")
     def test_5g_nsa_volte_call_forwarding_not_reachable(self):
@@ -2530,7 +2571,6 @@
             ads[2],
             call_forwarding_type="not_reachable")
 
-
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="e9a6027b-7dd1-4dca-a700-e4d42c9c947d")
     def test_call_waiting_scenario_1(self):
@@ -2546,16 +2586,14 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        return three_phone_call_waiting_short_seq(
-            self.log,
-            ads[0],
-            None,
-            None,
-            ads[1],
-            ads[2],
-            call_waiting=True,
-            scenario=1)
-
+        return three_phone_call_waiting_short_seq(self.log,
+                                                  ads[0],
+                                                  None,
+                                                  None,
+                                                  ads[1],
+                                                  ads[2],
+                                                  call_waiting=True,
+                                                  scenario=1)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="3fe02cb7-68d7-4762-882a-02bff8ce32f9")
@@ -2572,16 +2610,14 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        return three_phone_call_waiting_short_seq(
-            self.log,
-            ads[0],
-            None,
-            None,
-            ads[1],
-            ads[2],
-            call_waiting=True,
-            scenario=2)
-
+        return three_phone_call_waiting_short_seq(self.log,
+                                                  ads[0],
+                                                  None,
+                                                  None,
+                                                  ads[1],
+                                                  ads[2],
+                                                  call_waiting=True,
+                                                  scenario=2)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="bf5eb9ad-1fa2-468d-99dc-3cbcee8c89f8")
@@ -2598,16 +2634,14 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        return three_phone_call_waiting_short_seq(
-            self.log,
-            ads[0],
-            None,
-            None,
-            ads[1],
-            ads[2],
-            call_waiting=True,
-            scenario=3)
-
+        return three_phone_call_waiting_short_seq(self.log,
+                                                  ads[0],
+                                                  None,
+                                                  None,
+                                                  ads[1],
+                                                  ads[2],
+                                                  call_waiting=True,
+                                                  scenario=3)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="f2e4b6a9-6a6f-466c-884c-c0ef79d6ff01")
@@ -2624,16 +2658,14 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        return three_phone_call_waiting_short_seq(
-            self.log,
-            ads[0],
-            None,
-            None,
-            ads[1],
-            ads[2],
-            call_waiting=True,
-            scenario=4)
-
+        return three_phone_call_waiting_short_seq(self.log,
+                                                  ads[0],
+                                                  None,
+                                                  None,
+                                                  ads[1],
+                                                  ads[2],
+                                                  call_waiting=True,
+                                                  scenario=4)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="f2d36f45-63f6-4e01-9844-6fa53c26def7")
@@ -2650,16 +2682,14 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        return three_phone_call_waiting_short_seq(
-            self.log,
-            ads[0],
-            None,
-            None,
-            ads[1],
-            ads[2],
-            call_waiting=True,
-            scenario=5)
-
+        return three_phone_call_waiting_short_seq(self.log,
+                                                  ads[0],
+                                                  None,
+                                                  None,
+                                                  ads[1],
+                                                  ads[2],
+                                                  call_waiting=True,
+                                                  scenario=5)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="7eb2a89d-30ad-4a34-8e63-87d0181b91aa")
@@ -2676,16 +2706,14 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        return three_phone_call_waiting_short_seq(
-            self.log,
-            ads[0],
-            None,
-            None,
-            ads[1],
-            ads[2],
-            call_waiting=True,
-            scenario=6)
-
+        return three_phone_call_waiting_short_seq(self.log,
+                                                  ads[0],
+                                                  None,
+                                                  None,
+                                                  ads[1],
+                                                  ads[2],
+                                                  call_waiting=True,
+                                                  scenario=6)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="c63882e5-5b72-4ca6-8e36-260c50f42028")
@@ -2702,16 +2730,14 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        return three_phone_call_waiting_short_seq(
-            self.log,
-            ads[0],
-            None,
-            None,
-            ads[1],
-            ads[2],
-            call_waiting=True,
-            scenario=7)
-
+        return three_phone_call_waiting_short_seq(self.log,
+                                                  ads[0],
+                                                  None,
+                                                  None,
+                                                  ads[1],
+                                                  ads[2],
+                                                  call_waiting=True,
+                                                  scenario=7)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="f9be652f-a307-4fa5-9b30-ea78404110bd")
@@ -2728,16 +2754,14 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        return three_phone_call_waiting_short_seq(
-            self.log,
-            ads[0],
-            None,
-            None,
-            ads[1],
-            ads[2],
-            call_waiting=True,
-            scenario=8)
-
+        return three_phone_call_waiting_short_seq(self.log,
+                                                  ads[0],
+                                                  None,
+                                                  None,
+                                                  ads[1],
+                                                  ads[2],
+                                                  call_waiting=True,
+                                                  scenario=8)
 
     @TelephonyBaseTest.tel_test_wrap
     @test_tracker_info(uuid="b2e816b5-8e8f-4863-981c-47847d9527e0")
@@ -2752,11 +2776,10 @@
             self.log.error("Phone Failed to Set Up Properly.")
             return False
 
-        return three_phone_call_waiting_short_seq(
-            self.log,
-            ads[0],
-            None,
-            None,
-            ads[1],
-            ads[2],
-            call_waiting=False)
+        return three_phone_call_waiting_short_seq(self.log,
+                                                  ads[0],
+                                                  None,
+                                                  None,
+                                                  ads[1],
+                                                  ads[2],
+                                                  call_waiting=False)
diff --git a/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwActivationTest.py b/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwActivationTest.py
index fde9540..8bf2d03 100644
--- a/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwActivationTest.py
+++ b/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwActivationTest.py
@@ -17,8 +17,6 @@
     Test Script for 5G MSA mmWave Activation scenarios
 """
 
-import time
-
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_test_utils import reboot_device
@@ -56,9 +54,10 @@
             True if pass; False if fail.
         """
 
-        return test_activation_by_condition(self.android_devices[0],
-                                            nr_type='mmwave',
-                                            precond_func=lambda: cycle_airplane_mode(self.android_devices[0]))
+        return test_activation_by_condition(
+            self.android_devices[0],
+            nr_type='mmwave',
+            precond_func=lambda: cycle_airplane_mode(self.android_devices[0]))
 
     @test_tracker_info(uuid="21fb9b5c-40e8-4804-b05b-017395bb2e79")
     @TelephonyBaseTest.tel_test_wrap
@@ -74,9 +73,10 @@
             True if pass; False if fail.
         """
 
-        return test_activation_by_condition(self.android_devices[0],
-                                            nr_type='mmwave',
-                                            precond_func=lambda: reboot_device(self.android_devices[0]))
+        return test_activation_by_condition(
+            self.android_devices[0],
+            nr_type='mmwave',
+            precond_func=lambda: reboot_device(self.android_devices[0]))
 
     @test_tracker_info(uuid="2cef7ec0-ea74-458f-a98e-143d0be71f31")
     @TelephonyBaseTest.tel_test_wrap
@@ -98,4 +98,3 @@
                                             nr_type='mmwave')
 
     """ Tests End """
-
diff --git a/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwMmsTest.py b/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwMmsTest.py
index d865e8f..9cfcdb5 100755
--- a/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwMmsTest.py
+++ b/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwMmsTest.py
@@ -17,8 +17,6 @@
     Test Script for 5G NSA MMWAVE MMS scenarios
 """
 
-import time
-
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
@@ -41,10 +39,8 @@
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
 
-
     """ Tests Begin """
 
-
     @test_tracker_info(uuid="c6f7483f-6007-4a3b-a02d-5e6ab2b9a742")
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_nsa_mmw_mms_mo_mt(self):
@@ -58,14 +54,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmwave',
-            mt_rat='5g_nsa_mmwave',
-            msg_type='mms')
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmwave',
+                            mt_rat='5g_nsa_mmwave',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="8e6ed681-d5b8-4503-8262-a16739c66bdb")
     @TelephonyBaseTest.tel_test_wrap
@@ -80,14 +74,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmwave',
-            mt_rat='default',
-            msg_type='mms')
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmwave',
+                            mt_rat='default',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="d22ea7fd-6c07-4eb2-a1bf-10b03cab3201")
     @TelephonyBaseTest.tel_test_wrap
@@ -102,14 +94,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_nsa_mmwave',
-            msg_type='mms')
-
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_nsa_mmwave',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="897eb961-236d-4b8f-8a84-42f2010c6621")
     @TelephonyBaseTest.tel_test_wrap
@@ -125,14 +115,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmw_volte',
-            mt_rat='default',
-            msg_type='mms')
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_volte',
+                            mt_rat='default',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="6e185efe-b876-4dcf-9fc2-915039826dbe")
     @TelephonyBaseTest.tel_test_wrap
@@ -148,14 +136,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_nsa_mmw_volte',
-            msg_type='mms')
-
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_nsa_mmw_volte',
+                            msg_type='mms')
 
     @test_tracker_info(uuid="900d9913-b35d-4d75-859b-12bb28a35b73")
     @TelephonyBaseTest.tel_test_wrap
@@ -171,19 +157,20 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmw_wfc',
-            mt_rat='default',
-            msg_type='mms',
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
-
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_wfc',
+                            mt_rat='default',
+                            msg_type='mms',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="939a1ec5-1004-4527-b11e-eacbcfe0f632")
     @TelephonyBaseTest.tel_test_wrap
@@ -199,19 +186,20 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_nsa_mmw_wfc',
-            msg_type='mms',
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
-
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_nsa_mmw_wfc',
+                            msg_type='mms',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="253e4966-dd1c-487b-87fc-85b675140b24")
     @TelephonyBaseTest.tel_test_wrap
@@ -227,18 +215,19 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmw_wfc',
-            mt_rat='default',
-            msg_type='mms',
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
-
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_wfc',
+                            mt_rat='default',
+                            msg_type='mms',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="884435c5-47d8-4db9-b89e-087fc344a8b9")
     @TelephonyBaseTest.tel_test_wrap
@@ -254,18 +243,19 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_nsa_mmw_wfc',
-            msg_type='mms',
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
-
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_nsa_mmw_wfc',
+                            msg_type='mms',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="d0085f8f-bb18-4801-8bba-c5d2466922f2")
     @TelephonyBaseTest.tel_test_wrap
@@ -280,15 +270,13 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmwave',
-            mt_rat='5g_nsa_mmwave',
-            msg_type='mms',
-            long_msg=True)
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmwave',
+                            mt_rat='5g_nsa_mmwave',
+                            msg_type='mms',
+                            long_msg=True)
 
     @test_tracker_info(uuid="f43760c6-b040-46ba-9613-fde4192bf2db")
     @TelephonyBaseTest.tel_test_wrap
@@ -303,15 +291,13 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmwave',
-            mt_rat='default',
-            msg_type='mms',
-            long_msg=True)
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmwave',
+                            mt_rat='default',
+                            msg_type='mms',
+                            long_msg=True)
 
     @test_tracker_info(uuid="dc17e5d2-e022-47af-9b21-cf4e11911e17")
     @TelephonyBaseTest.tel_test_wrap
@@ -326,15 +312,13 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_nsa_mmwave',
-            msg_type='mms',
-            long_msg=True)
-
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_nsa_mmwave',
+                            msg_type='mms',
+                            long_msg=True)
 
     @test_tracker_info(uuid="2a73b511-988c-4a49-857c-5692f6d6cdd6")
     @TelephonyBaseTest.tel_test_wrap
@@ -350,16 +334,14 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmwave',
-            mt_rat='general',
-            msg_type='mms',
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmwave',
+                            mt_rat='general',
+                            msg_type='mms',
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="58414ce6-851a-4527-8243-502f5a8cfa7a")
     @TelephonyBaseTest.tel_test_wrap
@@ -375,14 +357,13 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='general',
-            mt_rat='5g_nsa_mmwave',
-            msg_type='mms',
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='general',
+                            mt_rat='5g_nsa_mmwave',
+                            msg_type='mms',
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     """ Tests End """
diff --git a/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwSmsTest.py b/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwSmsTest.py
index aeaac3b..cbd7df2 100755
--- a/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwSmsTest.py
+++ b/acts_tests/tests/google/nr/nsa5gmmw/Nsa5gMmwSmsTest.py
@@ -17,7 +17,6 @@
     Test Script for 5G NSA MMWAVE SMS scenarios
 """
 
-import time
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
@@ -40,10 +39,8 @@
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
 
-
     """ Tests Begin """
 
-
     @test_tracker_info(uuid="fb333cd5-2eaa-4d63-be26-fdf1e67d01b0")
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_nsa_mmw_sms_mo_mt(self):
@@ -57,13 +54,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmwave',
-            mt_rat='5g_nsa_mmwave')
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmwave',
+                            mt_rat='5g_nsa_mmwave')
 
     @test_tracker_info(uuid="3afc92e8-69f7-4ead-a416-4df9753da27a")
     @TelephonyBaseTest.tel_test_wrap
@@ -78,13 +73,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmwave',
-            mt_rat='default')
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmwave',
+                            mt_rat='default')
 
     @test_tracker_info(uuid="ee57da72-8e30-42ad-a7b3-d05bb4762724")
     @TelephonyBaseTest.tel_test_wrap
@@ -99,13 +92,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_nsa_mmwave')
-
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_nsa_mmwave')
 
     @test_tracker_info(uuid="1f75e117-f0f5-45fe-8896-91e0d2e61e9c")
     @TelephonyBaseTest.tel_test_wrap
@@ -121,13 +112,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmw_volte',
-            mt_rat='5g_nsa_mmw_volte')
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_volte',
+                            mt_rat='5g_nsa_mmw_volte')
 
     @test_tracker_info(uuid="f58fe4ed-77e0-40ff-8599-27d95cb27e14")
     @TelephonyBaseTest.tel_test_wrap
@@ -143,13 +132,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmw_volte',
-            mt_rat='default')
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_volte',
+                            mt_rat='default')
 
     @test_tracker_info(uuid="f60ac2b0-0feb-441e-9048-fe1b2878f8b6")
     @TelephonyBaseTest.tel_test_wrap
@@ -165,13 +152,11 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_nsa_mmw_volte')
-
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_nsa_mmw_volte')
 
     @test_tracker_info(uuid="6b27d804-abcd-4558-894d-545428a5dff4")
     @TelephonyBaseTest.tel_test_wrap
@@ -187,18 +172,19 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmw_wfc',
-            mt_rat='general',
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
-
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_wfc',
+                            mt_rat='general',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="0b848508-a1e8-4652-9e13-74749a7ccd2e")
     @TelephonyBaseTest.tel_test_wrap
@@ -214,18 +200,19 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='general',
-            mt_rat='5g_nsa_mmw_wfc',
-            is_airplane_mode=True,
-            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
-
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='general',
+                            mt_rat='5g_nsa_mmw_wfc',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="9fc07594-6dbf-4b7a-b5a5-f4c06032fa35")
     @TelephonyBaseTest.tel_test_wrap
@@ -242,17 +229,18 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmw_wfc',
-            mt_rat='general',
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
-
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_wfc',
+                            mt_rat='general',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="b76c0eaf-6e6b-4da7-87a0-26895f93a554")
     @TelephonyBaseTest.tel_test_wrap
@@ -269,17 +257,18 @@
         Returns:
             True if pass; False if fail.
         """
-        apm_mode = [toggle_airplane_mode(self.log, ad, False) for ad in self.android_devices]
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='general',
-            mt_rat='5g_nsa_mmw_wfc',
-            wfc_mode=WFC_MODE_WIFI_PREFERRED,
-            wifi_ssid=self.wifi_network_ssid,
-            wifi_pwd=self.wifi_network_pass)
-
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='general',
+                            mt_rat='5g_nsa_mmw_wfc',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     @test_tracker_info(uuid="43694343-e6f0-4430-972f-53f61c7b51b0")
     @TelephonyBaseTest.tel_test_wrap
@@ -294,14 +283,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmwave',
-            mt_rat='5g_nsa_mmwave',
-            long_msg=True)
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmwave',
+                            mt_rat='5g_nsa_mmwave',
+                            long_msg=True)
 
     @test_tracker_info(uuid="846dcf2d-911f-46a0-adb1-e32667b8ebd3")
     @TelephonyBaseTest.tel_test_wrap
@@ -317,14 +304,12 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[0],
-            self.android_devices[1],
-            mo_rat='5g_nsa_mmwave',
-            mt_rat='default',
-            long_msg=True)
-
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmwave',
+                            mt_rat='default',
+                            long_msg=True)
 
     @test_tracker_info(uuid="4d2951c3-d80c-4860-8dd9-9709cb7dfaa8")
     @TelephonyBaseTest.tel_test_wrap
@@ -340,12 +325,158 @@
             True if success.
             False if failed.
         """
-        return message_test(
-            self.log,
-            self.android_devices[1],
-            self.android_devices[0],
-            mo_rat='default',
-            mt_rat='5g_nsa_mmwave',
-            long_msg=True)
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_nsa_mmwave',
+                            long_msg=True)
+
+    @test_tracker_info(uuid="bee8b263-fe61-4a5c-a857-eb849426f0c7")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_5g_nsa_mmw_sms_mo_mt_in_call_volte(self):
+        """ Test MO SMS during a MO VoLTE call over 5G NSA MMW.
+
+        Provision devices on VoLTE
+        Provision devices in 5g NSA MMW
+        Make a Voice call from PhoneA to PhoneB
+        Send and Verify SMS from PhoneA to PhoneB
+        Verify both devices are still on 5g NSA
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_volte',
+                            mt_rat='5g_nsa_mmw_volte',
+                            msg_in_call=True)
+
+    @test_tracker_info(uuid="1a5ca91a-91f6-4b5d-b4b9-f436bb3870ff")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_5g_nsa_mmw_sms_mo_in_call_volte(self):
+        """ Test MO SMS during a MO VoLTE call over 5G NSA MMW.
+
+        Provision PhoneA on VoLTE
+        Provision PhoneA in 5g NSA MMW
+        Make a Voice call from PhoneA to PhoneB
+        Send and Verify SMS from PhoneA to PhoneB
+        Verify phoneA is still on 5g NSA
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_volte',
+                            mt_rat='default',
+                            msg_in_call=True)
+
+    @test_tracker_info(uuid="8a334ce7-a431-4fdc-bd65-f094d5ae727b")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_5g_nsa_mmw_sms_mt_in_call_volte(self):
+        """ Test MT SMS during a MT VoLTE call over 5G NSA.
+
+        Provision PhoneA on VoLTE
+        Provision PhoneA in 5g NSA MMW
+        Make a Voice call from PhoneB to PhoneA
+        Send and Verify SMS from PhoneB to PhoneA
+        Verify phoneA is still on 5g NSA
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return message_test(self.log,
+                            self.android_devices[1],
+                            self.android_devices[0],
+                            mo_rat='default',
+                            mt_rat='5g_nsa_mmw_volte',
+                            msg_in_call=True)
+
+    @test_tracker_info(uuid="ff29b86c-df34-4c6e-b880-baa321442fcf")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_5g_nsa_mmw_sms_mo_mt_iwlan(self):
+        """ Test SMS text function between two phones,
+        Phones in APM, WiFi connected, WFC Cell Preferred mode.
+
+        Disable APM on both devices
+        Provision devices in 5g NSA MMW
+        Provision devices for WFC Cell Pref with APM ON
+        Send and Verify SMS from PhoneA to PhoneB
+
+        Returns:
+            True if pass; False if fail.
+        """
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_wfc',
+                            mt_rat='5g_nsa_mmw_wfc',
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
+
+    @test_tracker_info(uuid="010a85d9-84d5-4893-aa43-478490bbf03c")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_5g_nsa_mmw_sms_mo_mt_iwlan_apm_off(self):
+        """ Test MO SMS, Phone in APM off, WiFi connected, WFC WiFi Preferred mode.
+
+        Disable APM on both devices
+        Provision devices in 5g NSA MMW
+        Provision devices for WFC Wifi Pref with APM OFF
+        Send and Verify SMS from PhoneA to PhoneB
+        Verify 5g NSA MMW attach for both devices
+
+        Returns:
+            True if pass; False if fail.
+        """
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_wfc',
+                            mt_rat='5g_nsa_mmw_wfc',
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
+
+    @test_tracker_info(uuid="40c11bd3-e0a2-46f9-b7f0-ccb8366e85b7")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_5g_nsa_mmw_sms_mo_mt_in_call_iwlan(self):
+        """ Test MO SMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode.
+
+        Disable APM on both devices
+        Provision devices in 5g NSA MMW
+        Provision devices for WFC Wifi Pref with APM ON
+        Make a Voice call from PhoneA to PhoneB
+        Send and Verify SMS from PhoneA to PhoneB
+
+        Returns:
+            True if pass; False if fail.
+        """
+        apm_mode = [
+            toggle_airplane_mode(self.log, ad, False)
+            for ad in self.android_devices
+        ]
+        return message_test(self.log,
+                            self.android_devices[0],
+                            self.android_devices[1],
+                            mo_rat='5g_nsa_mmw_wfc',
+                            mt_rat='5g_nsa_mmw_wfc',
+                            msg_in_call=True,
+                            is_airplane_mode=True,
+                            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+                            wifi_ssid=self.wifi_network_ssid,
+                            wifi_pwd=self.wifi_network_pass)
 
     """ Tests End """
diff --git a/acts_tests/tests/google/nr/sa5g/Sa5gMmsTest.py b/acts_tests/tests/google/nr/sa5g/Sa5gMmsTest.py
index b3be0b4..898b070 100755
--- a/acts_tests/tests/google/nr/sa5g/Sa5gMmsTest.py
+++ b/acts_tests/tests/google/nr/sa5g/Sa5gMmsTest.py
@@ -17,8 +17,6 @@
     Test Script for 5G SA MMS scenarios
 """
 
-import time
-
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
@@ -41,10 +39,8 @@
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
 
-
     """ Tests Begin """
 
-
     @test_tracker_info(uuid="74e2ae79-aee4-46e0-9326-fcd3b7f19128")
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_sa_mms_mo_mt(self):
@@ -71,7 +67,6 @@
         self.log.info("PASS - mms test over 5g sa validated")
         return True
 
-
     @test_tracker_info(uuid="6cd173f5-bd1d-44bb-aac2-ac63f37b9a62")
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_sa_mms_long_message_mo_mt(self):
@@ -96,7 +91,6 @@
 
         return _long_mms_test_mo(self.log, ads)
 
-
     @test_tracker_info(uuid="83d24fb5-1ebd-42e0-a3d1-b85b607234e2")
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_sa_mms_mo_mt_wifi(self):
@@ -125,5 +119,4 @@
 
         return _mms_test_mo(self.log, ads)
 
-
     """ Tests End """
diff --git a/acts_tests/tests/google/nr/sa5g/Sa5gSmsTest.py b/acts_tests/tests/google/nr/sa5g/Sa5gSmsTest.py
index fd84637..2593336 100755
--- a/acts_tests/tests/google/nr/sa5g/Sa5gSmsTest.py
+++ b/acts_tests/tests/google/nr/sa5g/Sa5gSmsTest.py
@@ -17,7 +17,6 @@
     Test Script for 5G SA SMS scenarios
 """
 
-import time
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
@@ -39,10 +38,8 @@
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
 
-
     """ Tests Begin """
 
-
     @test_tracker_info(uuid="8949d1c7-1719-4960-b79c-041b467fb5ef")
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_sa_sms_mo_mt(self):
@@ -69,7 +66,6 @@
         self.log.info("PASS - SMS test over 5G SA validated")
         return True
 
-
     @test_tracker_info(uuid="5c7a717b-1f98-44b7-95e7-0e83afb82a84")
     @TelephonyBaseTest.tel_test_wrap
     def test_5g_sa_sms_long_message_mo_mt(self):
diff --git a/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py b/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py
index 4f42ae0..27fca6b 100644
--- a/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py
+++ b/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py
@@ -25,6 +25,7 @@
 
 
 class PowerBLEadvertiseTest(PBtBT.PowerBTBaseTest):
+
     def __init__(self, configs):
         super().__init__(configs)
         req_params = ['adv_modes', 'adv_power_levels']
@@ -32,7 +33,9 @@
         # Loop all advertise modes and power levels
         for adv_mode in self.adv_modes:
             for adv_power_level in self.adv_power_levels:
-                self.generate_test_case(adv_mode, adv_power_level)
+                # As a temporary fix, set high power tests directly
+                if adv_power_level != 3:
+                    self.generate_test_case(adv_mode, adv_power_level)
 
     def setup_class(self):
 
@@ -58,3 +61,12 @@
                                    self.adv_duration)
         time.sleep(EXTRA_ADV_TIME)
         self.measure_power_and_validate()
+
+    def test_BLE_ADVERTISE_MODE_LOW_POWER_TX_POWER_HIGH(self):
+        self.measure_ble_advertise_power(0, 3)
+
+    def test_BLE_ADVERTISE_MODE_BALANCED_TX_POWER_HIGH(self):
+        self.measure_ble_advertise_power(1, 3)
+
+    def test_BLE_ADVERTISE_MODE_LOW_LATENCY_TX_POWER_HIGH(self):
+        self.measure_ble_advertise_power(2, 3)
diff --git a/acts_tests/tests/google/power/bt/PowerBLEscanTest.py b/acts_tests/tests/google/power/bt/PowerBLEscanTest.py
index 0aa5ad9..f76f796 100644
--- a/acts_tests/tests/google/power/bt/PowerBLEscanTest.py
+++ b/acts_tests/tests/google/power/bt/PowerBLEscanTest.py
@@ -30,9 +30,6 @@
         req_params = ['scan_modes']
         self.unpack_userparams(req_params)
 
-        for scan_mode in self.scan_modes:
-            self.generate_test_case_no_devices_around(scan_mode)
-
     def setup_class(self):
 
         super().setup_class()
@@ -54,3 +51,12 @@
         btputils.start_apk_ble_scan(self.dut, scan_mode, self.scan_duration)
         time.sleep(EXTRA_SCAN_TIME)
         self.measure_power_and_validate()
+
+    def test_BLE_SCAN_MODE_LOW_POWER_no_advertisers(self):
+        self.measure_ble_scan_power(0)
+
+    def test_BLE_SCAN_MODE_BALANCED_no_advertisers(self):
+        self.measure_ble_scan_power(1)
+
+    def test_BLE_SCAN_MODE_LOW_LATENCY_no_advertisers(self):
+        self.measure_ble_scan_power(2)
diff --git a/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py b/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py
index 419c418..7c882bd 100644
--- a/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py
+++ b/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py
@@ -39,7 +39,9 @@
         # Loop all codecs and tx power levels
         for codec_config in self.codecs:
             for tpl in self.tx_power_levels:
-                self.generate_test_case(codec_config, tpl)
+                # As a temporary fix, directly set the test with tpl 10
+                if tpl != 10:
+                    self.generate_test_case(codec_config, tpl)
 
     def setup_test(self):
         super().setup_test()
@@ -107,3 +109,9 @@
             codec_config['codec_type'], tpl))
         self.dut.droid.goToSleepNow()
         self.measure_power_and_validate()
+
+    def test_BTa2dp_AAC_codec_at_EPA_BF(self):
+        self.measure_a2dp_power(self.codecs[0], 10)
+
+    def test_BTa2dp_SBC_codec_at_EPA_BF(self):
+        self.measure_a2dp_power(self.codecs[1], 10)
\ No newline at end of file
diff --git a/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py b/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py
index c0bac23..db92276 100644
--- a/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py
+++ b/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py
@@ -73,3 +73,9 @@
         with open(self.log_file, 'w', newline='') as f:
             writer = csv.writer(f)
             writer.writerows(self.cal_matrix)
+
+    def teardown_test(self):
+        """
+        Clean up in teardown_test : Disable BQR
+        """
+        btutils.disable_bqr(self.dut)
diff --git a/acts_tests/tests/google/power/tel/PowerTelAirplaneMode_Test.py b/acts_tests/tests/google/power/tel/PowerTelAirplaneMode_Test.py
new file mode 100644
index 0000000..44fab02
--- /dev/null
+++ b/acts_tests/tests/google/power/tel/PowerTelAirplaneMode_Test.py
@@ -0,0 +1,36 @@
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+import time
+
+import acts_contrib.test_utils.power.cellular.cellular_power_preset_base_test as PB
+
+
+class PowerTelAirplaneModeTest(PB.PowerCellularPresetLabBaseTest):
+
+    def power_tel_airplane_mode_test(self):
+        """Measure power while airplane mode is on. """
+        # Start airplane mode
+        self.cellular_dut.toggle_airplane_mode(True)
+
+        # Allow airplane mode to propagate
+        time.sleep(3)
+
+        # Measure power
+        self.collect_power_data()
+        # Check if power measurement is within the required values
+        self.pass_fail_check(self.avg_current)
+
+class PowerTelAirplaneMode_Test(PowerTelAirplaneModeTest):
+    def test_airplane_mode(self):
+        self.power_tel_airplane_mode_test()
\ No newline at end of file
diff --git a/acts_tests/tests/google/power/tel/PowerTelIdle_Preset_Test.py b/acts_tests/tests/google/power/tel/PowerTelIdle_Preset_Test.py
new file mode 100644
index 0000000..8efbc76
--- /dev/null
+++ b/acts_tests/tests/google/power/tel/PowerTelIdle_Preset_Test.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import acts_contrib.test_utils.power.cellular.cellular_power_preset_base_test as PB
+
+
+class PowerTelIdle_Preset_Test(PB.PowerCellularPresetLabBaseTest):
+    def power_tel_idle_test(self):
+        """ Measures power when the device is on RRC idle state."""
+        idle_wait_time = self.simulation.rrc_sc_timer + 30
+        # Wait for RRC status change to trigger
+        self.cellular_simulator.wait_until_idle_state(idle_wait_time)
+
+        # Measure power
+        self.collect_power_data()
+
+        # Check if power measurement is below the required value
+        self.pass_fail_check(self.avg_current)
+
+    def test_preset_LTE_idle(self):
+        self.power_tel_idle_test()
+
+    def test_preset_sa_idle_fr1(self):
+        self.power_tel_idle_test()
diff --git a/acts_tests/tests/google/power/tel/PowerTelIms_Preset_Test.py b/acts_tests/tests/google/power/tel/PowerTelIms_Preset_Test.py
new file mode 100644
index 0000000..a18653d
--- /dev/null
+++ b/acts_tests/tests/google/power/tel/PowerTelIms_Preset_Test.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+#
+#   Copyright 20022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT 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.power.cellular.ims_api_connector_utils import ImsApiConnector
+import acts_contrib.test_utils.power.cellular.cellular_power_base_test as PWCEL
+from acts_contrib.test_utils.tel.tel_test_utils import set_phone_silent_mode
+from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+import acts_contrib.test_utils.power.cellular.cellular_power_preset_base_test as PB
+
+
+class PowerTelImsPresetTest(PB.PowerCellularPresetLabBaseTest):
+    ADB_CMD_ENABLE_IMS = ('am broadcast '
+        '-a com.google.android.carrier.action.LOCAL_OVERRIDE '
+        '-n com.google.android.carrier/.ConfigOverridingReceiver '
+        '--ez carrier_volte_available_bool true '
+        '--ez carrier_wfc_ims_available_bool true '
+        '--ez carrier_vt_available_bool true '
+        '--ez carrier_supports_ss_over_ut_bool true '
+        '--ez vonr_setting_visibility_bool true '
+        '--ez vonr_enabled_bool true')
+
+    ADB_CMD_DISABLE_IMS = ('am broadcast '
+        '-a com.google.android.carrier.action.LOCAL_OVERRIDE '
+        '-n com.google.android.carrier/.ConfigOverridingReceiver '
+        '--ez carrier_volte_available_bool false '
+        '--ez carrier_wfc_ims_available_bool false '
+        '--ez carrier_vt_available_bool false '
+        '--ez carrier_supports_ss_over_ut_bool false '
+        '--ez vonr_setting_visibility_bool false '
+        '--ez vonr_enabled_bool false')
+
+    # set NV command
+    # !NRCAPA.Gen.VoiceOverNr, 0, 01
+    ADB_SET_GOOG_NV = 'echo at+googsetnv="{nv_name}",{index},"{value}" > /dev/umts_router'
+
+    # Key IMS simulator default value
+    IMS_CLIENT_DEFAULT_IP = '127.0.0.1'
+    IMS_CLIENT_DEFAULT_PORT = 8250
+    IMS_CLIENT_DEFAULT_API_TOKEN = 'myclient'
+    IMS_API_CONNECTOR_DEFAULT_PORT = 5050
+
+    # IMS available app
+    IMS_CLIENT = 'client'
+    IMS_SERVER = 'server'
+
+    def setup_class(self):
+        """ Executed only once when initializing the class. """
+        super().setup_class()
+
+        # disable mobile data
+        self.log.info('Disable mobile data.')
+        self.dut.adb.shell('svc data disable')
+
+        # Enable IMS on UE
+        self.log.info('Enable VoLTE using adb command.')
+        self.dut.adb.shell(self.ADB_CMD_ENABLE_IMS)
+
+        # reboot device for settings to update
+        self.log.info('Reboot for VoLTE settings to update.')
+        self.dut.reboot()
+
+        # Set voice call volume to minimum
+        set_phone_silent_mode(self.log, self.dut)
+
+        # initialize ims simulator connector wrapper
+        self.unpack_userparams(api_connector_port=self.IMS_API_CONNECTOR_DEFAULT_PORT,
+                               api_token=self.IMS_CLIENT_DEFAULT_API_TOKEN,
+                               ims_client_ip=self.IMS_CLIENT_DEFAULT_IP,
+                               ims_client_port=self.IMS_CLIENT_DEFAULT_PORT)
+        self.ims_client = ImsApiConnector(
+            self.uxm_ip,
+            self.api_connector_port,
+            self.IMS_CLIENT,
+            self.api_token,
+            self.ims_client_ip,
+            self.ims_client_port,
+            self.log
+        )
+
+    def setup_test(self):
+        # Enable NR if it is VoNR test case
+        self.log.info(f'test name: {self.test_name}')
+        if 'NR' in self.test_name:
+            self.log.info('Enable VoNR for UE.')
+            self.enable_ims_nr()
+        super().setup_test()
+
+    def power_ims_call_test(self):
+        """ Measures power during a VoLTE call.
+
+        Measurement step in this test. Starts the voice call and
+        initiates power measurement. Pass or fail is decided with a
+        threshold value. """
+        # create dedicated bearer
+        self.log.info('create dedicated bearer.')
+        if 'LTE' in self.test_name:
+            self.cellular_simulator.create_dedicated_bearer()
+
+        time.sleep(5)
+
+        # Initiate the voice call
+        self.log.info('Callbox initiates call to UE.')
+        self.ims_client.initiate_call('001010123456789')
+
+        time.sleep(5)
+
+        # pick up call
+        self.log.info('UE pick up call.')
+        self.dut.adb.shell('input keyevent KEYCODE_CALL')
+
+        # Mute the call
+        self.dut.droid.telecomCallMute()
+
+        # Turn of screen
+        self.dut.droid.goToSleepNow()
+
+        # Measure power
+        self.collect_power_data()
+
+        # End the call
+        hangup_call(self.log, self.dut)
+
+        # Check if power measurement is within the required values
+        self.pass_fail_check()
+
+    def teardown_test(self):
+        super().teardown_test()
+        #self.cellular_simulator.deregister_ue_ims()
+        self.ims_client.remove_ims_app_link()
+
+    def teardown_class(self):
+        super().teardown_class()
+        self.log.info('Disable IMS.')
+        self.dut.adb.shell(self.ADB_CMD_DISABLE_IMS)
+
+
+class PowerTelIms_Preset_Test(PowerTelImsPresetTest):
+    def test_preset_LTE_voice(self):
+        self.power_ims_call_test()
+
+    def test_preset_NR_voice(self):
+        self.power_ims_call_test()
diff --git a/acts_tests/tests/google/power/tel/PowerTelPdcchFr2_Preset_Test.py b/acts_tests/tests/google/power/tel/PowerTelPdcchFr2_Preset_Test.py
new file mode 100644
index 0000000..3f023f3
--- /dev/null
+++ b/acts_tests/tests/google/power/tel/PowerTelPdcchFr2_Preset_Test.py
@@ -0,0 +1,36 @@
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+import acts_contrib.test_utils.power.cellular.cellular_power_preset_base_test as PB
+
+
+class PowerTelPdcchFr2_Preset_Test(PB.PowerCellularPresetLabBaseTest):
+    def power_pdcch_test(self):
+        """ Measures power during PDCCH only.
+
+        There's nothing to do here other than starting the power measurement
+        and deciding for pass or fail, as the base class will handle attaching.
+        Requirements for this test are that mac padding is off and that the
+        inactivity timer is not enabled. """
+
+        # Measure power
+        self.collect_power_data()
+
+        # Check if power measurement is within the required values
+        self.pass_fail_check(self.avg_current)
+
+    def test_preset_nsa_cdrx_fr2(self):
+        self.power_pdcch_test()
+
+    def test_preset_nsa_pdcch_fr2(self):
+        self.power_pdcch_test()
diff --git a/acts_tests/tests/google/power/tel/PowerTelPdcch_Modem_Test.py b/acts_tests/tests/google/power/tel/PowerTelPdcch_Modem_Test.py
index a8cdd49..d2623b9 100644
--- a/acts_tests/tests/google/power/tel/PowerTelPdcch_Modem_Test.py
+++ b/acts_tests/tests/google/power/tel/PowerTelPdcch_Modem_Test.py
@@ -47,6 +47,10 @@
         self.display_name_test_case = 'CDRxS10 - B41'
         self.power_pdcch_test()
 
+    def test_nr_1_n48_pdcch(self):
+        self.display_name_test_case = 'PDCCH 5G Sub6 NSA'
+        self.power_pdcch_test()
+
     def test_nr_1_n78_pdcch(self):
         self.display_name_test_case = 'PDCCH 5G Sub6 NSA'
         self.power_pdcch_test()
diff --git a/acts_tests/tests/google/power/tel/PowerTelPdcch_Preset_Test.py b/acts_tests/tests/google/power/tel/PowerTelPdcch_Preset_Test.py
new file mode 100644
index 0000000..2704638
--- /dev/null
+++ b/acts_tests/tests/google/power/tel/PowerTelPdcch_Preset_Test.py
@@ -0,0 +1,48 @@
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+import acts_contrib.test_utils.power.cellular.cellular_power_preset_base_test as PB
+
+class PowerTelPdcch_Preset_Test(PB.PowerCellularPresetLabBaseTest):
+    def power_pdcch_test(self):
+        """ Measures power during PDCCH only.
+
+        There's nothing to do here other than starting the power measurement
+        and deciding for pass or fail, as the base class will handle attaching.
+        Requirements for this test are that mac padding is off and that the
+        inactivity timer is not enabled. """
+
+        # Measure power
+        self.collect_power_data()
+
+        # Check if power measurement is within the required values
+        self.pass_fail_check(self.avg_current)
+
+    def test_preset_sa_pdcch_fr1(self):
+        self.power_pdcch_test()
+
+    def test_preset_nsa_pdcch_fr1(self):
+        self.power_pdcch_test()
+
+    def test_preset_LTE_pdcch(self):
+        self.power_pdcch_test()
+
+    def test_preset_sa_cdrx_fr1(self):
+        self.power_pdcch_test()
+
+    def test_preset_nsa_cdrx_fr1(self):
+        self.power_pdcch_test()
+
+    def test_preset_LTE_cdrx(self):
+        self.power_pdcch_test()
+
diff --git a/acts_tests/tests/google/power/tel/PowerTelTraffic_Preset_Test.py b/acts_tests/tests/google/power/tel/PowerTelTraffic_Preset_Test.py
new file mode 100644
index 0000000..d05c801
--- /dev/null
+++ b/acts_tests/tests/google/power/tel/PowerTelTraffic_Preset_Test.py
@@ -0,0 +1,178 @@
+#   Copyright 2022 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the 'License');
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an 'AS IS' BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+import os
+import time
+
+import acts_contrib.test_utils.power.cellular.cellular_power_preset_base_test as PB
+
+
+class PowerTelTrafficPresetTest(PB.PowerCellularPresetLabBaseTest):
+    # command to enable mobile data
+    ADB_CMD_ENABLE_MOBILE_DATA = 'svc data enable'
+
+    # command to start iperf server on UE
+    START_IPERF_SV_UE_CMD = 'nohup > /dev/null 2>&1 sh -c "iperf3 -s -i1 -p5201 > /dev/null  &"'
+
+    # command to start iperf server on UE
+    # (require: 1.path to iperf exe 2.hostname/hostIP)
+    START_IPERF_CLIENT_UE_CMD = 'nohup > /dev/null 2>&1 sh -c "iperf3 -c {iperf_host_ip} -i1 -p5202 -w8m -t2000 > /dev/null &"'
+
+    # command to start iperf server on host()
+    START_IPERF_SV_HOST_CMD = '{exe_path}\\iperf3 -s -p5202'
+
+    # command to start iperf client on host
+    # (require: 1.path to iperf exe 2.UE IP)
+    START_IPERF_CLIENT_HOST_CMD = (
+        '{exe_path}\\iperf3 -c {ue_ip} -w16M -t1000 -p5201')
+
+    START_IPERF_CLIENT_HOST_CMD_FR2 = (
+        '{exe_path}\\iperf3 -c {ue_ip} -w16M -t1000 -p5201 -P32')
+
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.ssh_iperf_client = None
+        self.ssh_iperf_server = None
+        self.iperf_out_err = {}
+
+    def setup_class(self):
+        super().setup_class()
+
+        # Unpack test parameters used in this class
+        self.unpack_userparams(iperf_exe_path=None,
+                               ue_ip=None,
+                               iperf_host_ip=None)
+
+        # Verify required config
+        for param in ('iperf_exe_path', 'ue_ip', 'iperf_host_ip'):
+            if getattr(self, param) is None:
+                raise RuntimeError(
+                    f'Parameter "{param}" is required to run this type of test')
+
+    def setup_test(self):
+        # Call parent method first to setup simulation
+        super().setup_test()
+
+        # setup ssh client
+        self.ssh_iperf_client = self.cellular_simulator.create_ssh_client()
+        self.ssh_iperf_server = self.cellular_simulator.create_ssh_client()
+
+        self.turn_on_mobile_data()
+
+    def power_tel_traffic_test(self):
+        """Measure power while data is transferring."""
+        # Start data traffic
+        self.start_uplink_process()
+        time.sleep(5)
+        self.start_downlink_process()
+
+        # Measure power
+        self.collect_power_data()
+
+        # Write iperf log
+        self.ssh_iperf_server.close()
+        uplink_log_name = self.test_name + '_uplink.txt'
+        self._write_iperf_log(uplink_log_name, self.ssh_iperf_server)
+        self.ssh_iperf_client.close()
+        downlink_log_name = self.test_name + '_downlink.txt'
+        self._write_iperf_log(downlink_log_name, self.ssh_iperf_client)
+
+    def _exec_ssh_cmd(self, ssh_client, cmd):
+        """Execute command on given ssh client.
+
+        Args:
+            ssh_client: parmiko ssh client object.
+            cmd: command to execute via ssh.
+        """
+        self.log.info('Sending cmd to ssh host: ' + cmd)
+        stdin, stdout, stderr = ssh_client.exec_command(cmd, get_pty=True)
+        stdin.close()
+        self.iperf_out_err[ssh_client] = (stdout, stderr)
+
+    def start_downlink_process(self):
+        """UE transfer data to host."""
+        self.log.info('Start downlink process')
+        # start UE iperf server
+        self.cellular_dut.ad.adb.shell(self.START_IPERF_SV_UE_CMD)
+        self.log.info('cmd sent to UE: ' + self.START_IPERF_SV_UE_CMD)
+        self.log.info('UE iperf server started')
+        time.sleep(5)
+        # start host iperf client
+        cmd = None
+        if 'fr2' in self.test_name:
+            cmd = self.START_IPERF_CLIENT_HOST_CMD_FR2.format(
+                exe_path=self.iperf_exe_path,
+                ue_ip=self.ue_ip)
+        else:
+            cmd = self.START_IPERF_CLIENT_HOST_CMD.format(
+                exe_path=self.iperf_exe_path,
+                ue_ip=self.ue_ip)
+
+        if not cmd:
+            raise RuntimeError('Cannot format command to start iperf client.')
+        self._exec_ssh_cmd(self.ssh_iperf_client, cmd)
+        self.log.info('Host iperf client started')
+        time.sleep(5)
+
+    def start_uplink_process(self):
+        """Host transfer data to UE."""
+        self.log.info('Start uplink process')
+        # start host iperf server
+        cmd = self.START_IPERF_SV_HOST_CMD.format(exe_path=self.iperf_exe_path)
+        self._exec_ssh_cmd(self.ssh_iperf_server, cmd)
+        self.log.info('Host iperf server started')
+        time.sleep(5)
+        # start UE iperf
+        adb_cmd = self.START_IPERF_CLIENT_UE_CMD.format(
+            iperf_host_ip=self.iperf_host_ip)
+        self.cellular_dut.ad.adb.shell(adb_cmd)
+        self.log.info('cmd sent to UE: ' + adb_cmd)
+        self.log.info('UE iperf client started')
+        time.sleep(5)
+
+    def _write_iperf_log(self, file_name, ssh):
+        """ Writing ssh stdout and stdin to log file.
+
+        Args:
+            file_name: log file name to write log to.
+            ssh: paramiko client object.
+        """
+        iperf_log_dir = os.path.join(self.root_output_path, 'iperf')
+        os.makedirs(iperf_log_dir, exist_ok=True)
+        iperf_log_file_path = os.path.join(iperf_log_dir, file_name)
+        with open(iperf_log_file_path, 'w') as f:
+            out, err = self.iperf_out_err[ssh]
+            out_content = ''.join(out.readlines())
+            err_content = ''.join(err.readlines())
+            f.write(out_content)
+            f.write('\nErrors:\n')
+            f.write(err_content)
+
+    def turn_on_mobile_data(self):
+        self.dut.adb.shell(self.ADB_CMD_ENABLE_MOBILE_DATA)
+
+
+class PowerTelTraffic_Preset_Test(PowerTelTrafficPresetTest):
+    def test_preset_LTE_traffic(self):
+        self.power_tel_traffic_test()
+
+    def test_preset_nsa_traffic_fr1(self):
+        self.power_tel_traffic_test()
+
+    def test_preset_sa_traffic_fr1(self):
+        self.power_tel_traffic_test()
+
+
+class PowerTelTrafficFr2_Preset_Test(PowerTelTrafficPresetTest):
+    def test_preset_nsa_traffic_fr2(self):
+        self.power_tel_traffic_test()
diff --git a/acts_tests/tests/google/power/wifi/PowerWiFiHotspotTest.py b/acts_tests/tests/google/power/wifi/PowerWiFiHotspotTest.py
index 7e7c3b8..e0013b1 100644
--- a/acts_tests/tests/google/power/wifi/PowerWiFiHotspotTest.py
+++ b/acts_tests/tests/google/power/wifi/PowerWiFiHotspotTest.py
@@ -23,6 +23,7 @@
 from acts_contrib.test_utils.wifi import wifi_power_test_utils as wputils
 from acts_contrib.test_utils.power.IperfHelper import IperfHelper
 
+WAIT_TIME_BEFORE_CONNECT = 10
 
 class PowerWiFiHotspotTest(PWBT.PowerWiFiBaseTest):
     """ WiFi Tethering HotSpot Power test.
@@ -60,6 +61,7 @@
         Configures the Hotspot SSID
         """
         super().setup_class()
+        self.set_attenuation([0, 0, 0, 0])
 
         # If an SSID and password are indicated in the configuration parameters,
         # use those. If not, use default parameters and warn the user.
@@ -101,6 +103,15 @@
         super().setup_test()
         wutils.reset_wifi(self.android_devices[1])
 
+    def teardown_test(self):
+        # Tearing down tethering on dut
+        if self.dut.droid.isSdkAtLeastS():
+            hotspot_cmd = "cmd wifi stop-softap " + str(self.network[wutils.WifiEnums.SSID_KEY]) + \
+                          " wpa2 " + str(self.network[wutils.WifiEnums.PWD_KEY]) + " -b " + \
+                          str(list(self.test_configs.band)[0])
+            self.dut.adb.shell(hotspot_cmd)
+            wutils.reset_wifi(self.android_devices[1])
+
     def setup_hotspot(self, connect_client=False):
         """Configure Hotspot and connects client device.
 
@@ -134,12 +145,21 @@
                 self.main_network[self.test_configs.wifi_sharing])
 
         # Setup tethering on dut
-        wutils.start_wifi_tethering(
-            self.dut, self.network[wutils.WifiEnums.SSID_KEY],
-            self.network[wutils.WifiEnums.PWD_KEY], wifi_band_id)
+        if self.dut.droid.isSdkAtLeastS():
+            # Setup tethering on dut with adb command.
+            hotspot_cmd = "cmd wifi start-softap " + str(self.network[wutils.WifiEnums.SSID_KEY]) + \
+                          " wpa2 " + str(self.network[wutils.WifiEnums.PWD_KEY]) + " -b " + \
+                          str(list(self.test_configs.band)[0])
+            self.log.info(str(hotspot_cmd))
+            self.dut.adb.shell(hotspot_cmd)
+        else:
+            wutils.start_wifi_tethering(
+                self.dut, self.network[wutils.WifiEnums.SSID_KEY],
+                self.network[wutils.WifiEnums.PWD_KEY], wifi_band_id)
 
         # Connect client device to Hotspot
         if connect_client:
+            time.sleep(WAIT_TIME_BEFORE_CONNECT)
             wutils.wifi_connect(
                 self.android_devices[1],
                 self.network,
@@ -168,7 +188,14 @@
             self.client_iperf_helper.process_iperf_results(
                 self.dut, self.log, self.iperf_servers, self.test_name)
 
-        self.pass_fail_check(self.avg_current)
+        if hasattr(self, 'bitses'):
+            """
+            If measurement is taken through BITS, metric value is avg_power,
+            else metric value is avg_current.
+            """
+            self.pass_fail_check(self.power_result.metric_value)
+        else:
+            self.pass_fail_check(self.avg_current)
 
     def power_idle_tethering_test(self):
         """ Start power test when Hotspot is idle
diff --git a/acts_tests/tests/google/power/wifi/PowerWiFiscanTest.py b/acts_tests/tests/google/power/wifi/PowerWiFiscanTest.py
index 72f733f..da1c67d 100644
--- a/acts_tests/tests/google/power/wifi/PowerWiFiscanTest.py
+++ b/acts_tests/tests/google/power/wifi/PowerWiFiscanTest.py
@@ -18,10 +18,20 @@
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.power import PowerWiFiBaseTest as PWBT
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
+from acts_contrib.test_utils.wifi.wifi_power_test_utils import CHRE_WIFI_SCAN_TYPE
+from acts_contrib.test_utils.wifi.wifi_power_test_utils import CHRE_WIFI_RADIO_CHAIN
+from acts_contrib.test_utils.wifi.wifi_power_test_utils import CHRE_WIFI_CHANNEL_SET
 
 UNLOCK_SCREEN = 'input keyevent 82'
 LOCATION_ON = 'settings put secure location_mode 3'
-
+ENABLE_WIFI_SCANNING = 'cmd wifi enable-scanning enabled'
+DISABLE_WIFI_SCANNING = 'cmd wifi enable-scanning disabled'
+CHRE_POWER_TEST_CLIENT = 'chre_power_test_client'
+UNLOAD_ALL_CHRE_PRODUCTION_APP = CHRE_POWER_TEST_CLIENT + ' unloadall'
+LOAD_CHRE_TEST_NANOAPP = CHRE_POWER_TEST_CLIENT + ' load'
+DISABLE_CHRE_WIFI_SCAN = CHRE_POWER_TEST_CLIENT + ' wifi disable'
+CHRE_SCAN_INTERVAL = 5
+NS = 1000000000
 
 class PowerWiFiscanTest(PWBT.PowerWiFiBaseTest):
     def setup_class(self):
@@ -71,6 +81,25 @@
                 atten_setting = self.test_configs.wifi_band + '_roaming'
                 self.set_attenuation(self.atten_level[atten_setting])
 
+    def chre_scan_setup(self):
+        self.dut.adb.shell("cmd wifi force-country-code enabled US")
+        time.sleep(1)
+        country_code = self.dut.adb.shell("cmd wifi get-country-code")
+        if "US" in country_code:
+            self.log.info("Country-Code is set to US")
+        else:
+            self.log.warning("Country Code is : " + str(country_code))
+        self.dut.adb.shell(DISABLE_WIFI_SCANNING)
+        self.dut.adb.shell(UNLOAD_ALL_CHRE_PRODUCTION_APP)
+        self.dut.adb.shell(LOAD_CHRE_TEST_NANOAPP)
+        chre_wifi_scan_trigger_cmd = CHRE_POWER_TEST_CLIENT + ' wifi enable ' + \
+                                     str(CHRE_SCAN_INTERVAL*NS) + \
+                                     " " + CHRE_WIFI_SCAN_TYPE[self.test_configs.wifi_scan_type] + " " + \
+                                     CHRE_WIFI_RADIO_CHAIN[self.test_configs.wifi_radio_chain] + " " + \
+                                     CHRE_WIFI_CHANNEL_SET[self.test_configs.wifi_channel_set]
+        self.dut.log.info(chre_wifi_scan_trigger_cmd)
+        self.dut.adb.shell(chre_wifi_scan_trigger_cmd)
+
     def wifi_scan_test_func(self):
 
         attrs = [
@@ -94,6 +123,22 @@
         self.scan_setup()
         self.measure_power_and_validate()
 
+    def chre_wifi_scan_test_func(self):
+        attrs = [
+            'screen_status', 'wifi_scan_type', 'wifi_radio_chain', 'wifi_channel_set'
+        ]
+        indices = [2, 6, 7, 8]
+        self.decode_test_configs(attrs, indices)
+        wutils.wifi_toggle_state(self.dut, True)
+        if self.test_configs.screen_status == 'OFF':
+            self.dut.droid.goToSleepNow()
+            self.dut.log.info('Screen is OFF')
+        time.sleep(5)
+        self.chre_scan_setup()
+        self.measure_power_and_validate()
+        self.dut.adb.shell(DISABLE_CHRE_WIFI_SCAN)
+        self.dut.adb.shell(ENABLE_WIFI_SCANNING)
+
     # Test cases
     # Power.apk triggered singleshot scans
     @test_tracker_info(uuid='e5539b01-e208-43c6-bebf-6f1e73d8d8cb')
@@ -157,3 +202,52 @@
 
         """
         self.wifi_scan_test_func()
+
+    def test_screen_OFF_CHRE_wifi_scan_activePassiveDfs_highAccuracy_all(self):
+        """
+        Trigger CHRE based scan for the following parameters :
+        wifi_scan_type : activePassiveDfs
+        wifi_radio_chain : highAccuracy
+        wifi_channel_set : all
+        """
+        self.chre_wifi_scan_test_func()
+
+
+    def test_screen_OFF_CHRE_wifi_scan_activePassiveDfs_lowLatency_all(self):
+        """
+        Trigger CHRE based scan for the following parameters :
+        wifi_scan_type : activePassiveDfs
+        wifi_radio_chain : lowLatency
+        wifi_channel_set : all
+        """
+        self.chre_wifi_scan_test_func()
+
+
+    def test_screen_OFF_CHRE_wifi_scan_noPreference_lowPower_all(self):
+        """
+        Trigger CHRE based scan for the following parameters :
+        wifi_scan_type : noPreference
+        wifi_radio_chain : lowPower
+        wifi_channel_set : all
+        """
+        self.chre_wifi_scan_test_func()
+
+
+    def test_screen_OFF_CHRE_wifi_scan_passive_lowLatency_all(self):
+        """
+        Trigger CHRE based scan for the following parameters :
+        wifi_scan_type : passive
+        wifi_radio_chain : lowLatency
+        wifi_channel_set : all
+        """
+        self.chre_wifi_scan_test_func()
+
+
+    def test_screen_OFF_CHRE_wifi_scan_passive_highAccuracy_all(self):
+        """
+        Trigger CHRE based scan for the following parameters :
+        wifi_scan_type : passive
+        wifi_radio_chain : highAccuracy
+        wifi_channel_set : all
+        """
+        self.chre_wifi_scan_test_func()
\ No newline at end of file
diff --git a/acts_tests/tests/google/tel/lab/TelLabCmasTest.py b/acts_tests/tests/google/tel/lab/TelLabCmasTest.py
index 052adb8..2e9bb84 100644
--- a/acts_tests/tests/google/tel/lab/TelLabCmasTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabCmasTest.py
@@ -16,7 +16,6 @@
 """
 Sanity tests for voice tests in telephony
 """
-import time
 
 from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
 from acts.controllers.anritsu_lib.md8475a import CBCHSetup
@@ -147,12 +146,11 @@
                 self.log.error("No valid RAT provided for CMAS test.")
                 return False
 
-            if not ensure_network_rat(
-                    self.log,
-                    self.ad,
-                    preferred_network_setting,
-                    rat_family,
-                    toggle_apm_after_setting=True):
+            if not ensure_network_rat(self.log,
+                                      self.ad,
+                                      preferred_network_setting,
+                                      rat_family,
+                                      toggle_apm_after_setting=True):
                 self.log.error(
                     "Failed to set rat family {}, preferred network:{}".format(
                         rat_family, preferred_network_setting))
@@ -164,8 +162,9 @@
                         self.log, self.ad, self.anritsu,
                         next(TelLabCmasTest.SERIAL_NO), message_id,
                         warning_message):
-                    self.log.warning("Phone {} Failed to receive CMAS message"
-                                     .format(self.ad.serial))
+                    self.log.warning(
+                        "Phone {} Failed to receive CMAS message".format(
+                            self.ad.serial))
                     # Another check of logcat before confirming failure
                     if self.ad.search_logcat(warning_message):
                         self.ad.log.info(
@@ -179,8 +178,9 @@
                         next(TelLabCmasTest.SERIAL_NO), message_id,
                         warning_message, c2k_response_type, c2k_severity,
                         c2k_urgency, c2k_certainty):
-                    self.log.warning("Phone {} Failed to receive CMAS message"
-                                     .format(self.ad.serial))
+                    self.log.warning(
+                        "Phone {} Failed to receive CMAS message".format(
+                            self.ad.serial))
                     if self.ad.search_logcat(warning_message):
                         self.ad.log.info(
                             "Confirmed from Logcat - User received %s",
@@ -234,9 +234,9 @@
         Returns:
             True if pass; False if fail
         """
-        return self._send_receive_cmas_message(set_system_model_lte, RAT_LTE,
-                                               CMAS_MESSAGE_PRESIDENTIAL_ALERT,
-                                               "LTE CMAS Presidential Alert")
+        return self._send_receive_cmas_message(
+            set_system_model_lte, RAT_LTE, CMAS_MESSAGE_PRESIDENTIAL_ALERT,
+            "LTE CMAS Presidential Alert")
 
     @test_tracker_info(uuid="33be2aaa-e8a6-4832-afea-8bd7e5555cc7")
     @TelephonyBaseTest.tel_test_wrap
@@ -434,9 +434,10 @@
         Returns:
             True if pass; False if fail
         """
-        return self._send_receive_cmas_message(
-            set_system_model_1x_evdo, RAT_1XRTT,
-            CMAS_C2K_CATEGORY_PRESIDENTIAL, "1X CMAS Presidential Alert")
+        return self._send_receive_cmas_message(set_system_model_1x_evdo,
+                                               RAT_1XRTT,
+                                               CMAS_C2K_CATEGORY_PRESIDENTIAL,
+                                               "1X CMAS Presidential Alert")
 
     @test_tracker_info(uuid="d1283544-81d0-4852-9387-c94826794896")
     @TelephonyBaseTest.tel_test_wrap
@@ -502,9 +503,9 @@
         Returns:
             True if pass; False if fail
         """
-        return self._send_receive_cmas_message(set_system_model_gsm, RAT_GSM,
-                                               CMAS_MESSAGE_PRESIDENTIAL_ALERT,
-                                               "GSM CMAS Presidential Alert")
+        return self._send_receive_cmas_message(
+            set_system_model_gsm, RAT_GSM, CMAS_MESSAGE_PRESIDENTIAL_ALERT,
+            "GSM CMAS Presidential Alert")
 
     @test_tracker_info(uuid="c6d6b57b-c915-46e3-acbe-4d7f8cd6e52e")
     @TelephonyBaseTest.tel_test_wrap
diff --git a/acts_tests/tests/google/tel/lab/TelLabEtwsTest.py b/acts_tests/tests/google/tel/lab/TelLabEtwsTest.py
index b3a566c..a23d385 100644
--- a/acts_tests/tests/google/tel/lab/TelLabEtwsTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabEtwsTest.py
@@ -16,7 +16,6 @@
 """
 Sanity tests for voice tests in telephony
 """
-import time
 
 from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
 from acts.controllers.anritsu_lib.md8475a import MD8475A
@@ -124,12 +123,11 @@
                 self.log.error("No valid RAT provided for ETWS test.")
                 return False
 
-            if not ensure_network_rat(
-                    self.log,
-                    self.ad,
-                    preferred_network_setting,
-                    rat_family,
-                    toggle_apm_after_setting=True):
+            if not ensure_network_rat(self.log,
+                                      self.ad,
+                                      preferred_network_setting,
+                                      rat_family,
+                                      toggle_apm_after_setting=True):
                 self.log.error(
                     "Failed to set rat family {}, preferred network:{}".format(
                         rat_family, preferred_network_setting))
@@ -140,8 +138,9 @@
                     self.log, self.ad, self.anritsu,
                     next(TelLabEtwsTest.SERIAL_NO), message_id,
                     warning_message):
-                self.log.error("Phone {} Failed to receive ETWS message"
-                               .format(self.ad.serial))
+                self.log.error(
+                    "Phone {} Failed to receive ETWS message".format(
+                        self.ad.serial))
                 return False
         except AnritsuError as e:
             self.log.error("Error in connection with Anritsu Simulator: " +
@@ -237,9 +236,10 @@
         Returns:
             True if pass; False if fail
         """
-        return self._send_receive_etws_message(
-            set_system_model_wcdma, RAT_WCDMA, ETWS_WARNING_EARTHQUAKETSUNAMI,
-            "WCDMA Earthquake and Tsunami")
+        return self._send_receive_etws_message(set_system_model_wcdma,
+                                               RAT_WCDMA,
+                                               ETWS_WARNING_EARTHQUAKETSUNAMI,
+                                               "WCDMA Earthquake and Tsunami")
 
     @test_tracker_info(uuid="71dc9650-d00a-4533-99f5-5cc301c21334")
     @TelephonyBaseTest.tel_test_wrap
@@ -260,9 +260,10 @@
         Returns:
             True if pass; False if fail
         """
-        return self._send_receive_etws_message(
-            set_system_model_wcdma, RAT_WCDMA, ETWS_WARNING_OTHER_EMERGENCY,
-            "WCDMA ETWS Other Emergency")
+        return self._send_receive_etws_message(set_system_model_wcdma,
+                                               RAT_WCDMA,
+                                               ETWS_WARNING_OTHER_EMERGENCY,
+                                               "WCDMA ETWS Other Emergency")
 
     @test_tracker_info(uuid="a9fd9c0e-21bf-41d1-81d2-c34679052fe0")
     @TelephonyBaseTest.tel_test_wrap
diff --git a/acts_tests/tests/google/tel/lab/TelLabGFTDSDSInOutServiceTest.py b/acts_tests/tests/google/tel/lab/TelLabGFTDSDSInOutServiceTest.py
index a4529eb..b0ce3b3 100644
--- a/acts_tests/tests/google/tel/lab/TelLabGFTDSDSInOutServiceTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabGFTDSDSInOutServiceTest.py
@@ -15,30 +15,30 @@
 #   limitations under the License.
 
 import time
-import datetime
 from acts import asserts
 from acts.test_decorators import test_tracker_info
 from acts.libs.utils.multithread import multithread_func
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.GFTInOutBaseTest import GFTInOutBaseTest
-from acts_contrib.test_utils.tel.gft_inout_utils import mo_voice_call
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_ims_registered
+from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
+from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_ims_registered
 from acts_contrib.test_utils.tel.tel_data_utils import active_file_download_test
-from acts_contrib.test_utils.tel.tel_test_utils import ensure_phones_idle
+from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
 from acts_contrib.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
-
+from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts_contrib.test_utils.tel.tel_data_utils import wait_for_network_service
 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_voice_sub_id
+from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
 
+from acts_contrib.test_utils.tel.gft_inout_utils import mo_voice_call
 from acts_contrib.test_utils.tel.gft_inout_defines import VOICE_CALL
 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.gft_inout_defines import NO_SERVICE_POWER_LEVEL
 from acts_contrib.test_utils.tel.gft_inout_defines import IN_SERVICE_POWER_LEVEL
-from acts_contrib.test_utils.tel.gft_inout_utils import check_ims_state
-from acts_contrib.test_utils.tel.tel_test_utils import wait_for_network_service
 
 IDLE_CASE = 1
 DATA_TRANSFER_CASE = 2
@@ -47,6 +47,7 @@
 CALL_DATA_CASE = 5
 _VOLTE = "volte"
 
+
 class TelLabGFTDSDSInOutServiceTest(GFTInOutBaseTest):
     def __init__(self, controllers):
         GFTInOutBaseTest.__init__(self, controllers)
@@ -56,8 +57,15 @@
         GFTInOutBaseTest.teardown_class(self)
         ensure_phones_idle(self.log, self.android_devices)
 
-    def _dsds_in_out_service_test(self, case=IDLE_CASE, loop=1, idle_time=60, dds_slot=0,
-        voice_slot=0, sms_slot=0, psim_rat=_VOLTE , esim_rat=_VOLTE):
+    def _dsds_in_out_service_test(self,
+                                  case=IDLE_CASE,
+                                  loop=1,
+                                  idle_time=60,
+                                  dds_slot=0,
+                                  voice_slot=0,
+                                  sms_slot=0,
+                                  psim_rat=_VOLTE,
+                                  esim_rat=_VOLTE):
         '''
             b/201599180
             Move UE from coverage area to no service area and UE shows no service
@@ -78,7 +86,8 @@
             Raises:
                 TestFailure if not success.
         '''
-        tasks = [(set_dds_on_slot, (ad, dds_slot )) for ad in self.android_devices]
+        tasks = [(set_dds_on_slot, (ad, dds_slot))
+                 for ad in self.android_devices]
         if not multithread_func(self.log, tasks):
             asserts.skip("Fail to to set up DDS")
         for ad in self.android_devices:
@@ -86,51 +95,67 @@
             if voice_sub_id == INVALID_SUB_ID:
                 asserts.skip("Failed to get sub ID ar slot %s.", voice_slot)
             else:
-                ad.log.info("get_subid_from_slot_index voice_slot=%s. voice_sub_id=%s"
-                    , voice_slot, voice_sub_id)
+                ad.log.info(
+                    "get_subid_from_slot_index voice_slot=%s. voice_sub_id=%s",
+                    voice_slot, voice_sub_id)
             if not set_voice_sub_id(ad, voice_sub_id):
-                ad.log.info("Fail to to set voice to slot %s" , voice_sub_id)
+                ad.log.info("Fail to to set voice to slot %s", voice_sub_id)
             else:
-                ad.log.info("set voice to slot %s" , voice_sub_id)
-        tasks = [(set_message_subid, (ad, sms_slot )) for ad in self.android_devices]
+                ad.log.info("set voice to slot %s", voice_sub_id)
+        tasks = [(set_message_subid, (ad, sms_slot))
+                 for ad in self.android_devices]
         if multithread_func(self.log, tasks):
-            asserts.skip("Fail to to set up sms to slot %s" , sms_slot)
+            asserts.skip("Fail to to set up sms to slot %s", sms_slot)
         else:
-            ad.log.info("set up sms to slot %s" , sms_slot)
+            ad.log.info("set up sms to slot %s", sms_slot)
 
-        for x in range (loop):
-            self.log.info("%s loop: %s/%s" , self.current_test_name, x+1, loop))
+        for x in range(loop):
+            self.log.info("%s loop: %s/%s", self.current_test_name, x + 1,
+                          loop)
             if case == IDLE_CASE:
-                asserts.assert_true(self._dsds_in_out_service_idle_test(idle_time),
-                    "Fail: %s." %("_dsds_in_out_service_idle_test failure"),
+                asserts.assert_true(
+                    self._dsds_in_out_service_idle_test(idle_time),
+                    "Fail: %s." % ("_dsds_in_out_service_idle_test failure"),
                     extras={"failure_cause": self.my_error_msg})
             elif case == DATA_TRANSFER_CASE:
-                asserts.assert_true(self._dsds_in_out_service_data_transfer_test(idle_time),
-                    "Fail: %s." %("_dsds_in_out_service_data_transfer_test failure"),
+                asserts.assert_true(
+                    self._dsds_in_out_service_data_transfer_test(idle_time),
+                    "Fail: %s." %
+                    ("_dsds_in_out_service_data_transfer_test failure"),
                     extras={"failure_cause": self.my_error_msg})
             elif case == DATA_OFF_CASE:
-                asserts.assert_true(self._dsds_in_out_service_data_off_test(idle_time),
-                    "Fail: %s." %("_dsds_in_out_service_data_off_test failure"),
+                asserts.assert_true(
+                    self._dsds_in_out_service_data_off_test(idle_time),
+                    "Fail: %s." %
+                    ("_dsds_in_out_service_data_off_test failure"),
                     extras={"failure_cause": self.my_error_msg})
             elif case == IN_CALL_CASE:
-                asserts.assert_true(self._dsds_in_out_service_in_call_test(idle_time),
-                    "Fail: %s." %("_dsds_in_out_service_in_call_test failure"),
+                asserts.assert_true(
+                    self._dsds_in_out_service_in_call_test(idle_time),
+                    "Fail: %s." %
+                    ("_dsds_in_out_service_in_call_test failure"),
                     extras={"failure_cause": self.my_error_msg})
             elif case == CALL_DATA_CASE:
-                asserts.assert_true(self._dsds_in_out_service_in_call_transfer_test(idle_time),
-                    "Fail: %s." %("_dsds_in_out_service_in_call_transfer_test failure"),
+                asserts.assert_true(
+                    self._dsds_in_out_service_in_call_transfer_test(idle_time),
+                    "Fail: %s." %
+                    ("_dsds_in_out_service_in_call_transfer_test failure"),
                     extras={"failure_cause": self.my_error_msg})
 
-            tasks = [(wait_for_network_service, (self.log, ad, ))
-                for ad in self.android_devices]
+            tasks = [(wait_for_network_service, (
+                self.log,
+                ad,
+            )) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                asserts.assert_true(False, "Fail: %s." %("wait_for_network_service failure"),
+                asserts.assert_true(
+                    False,
+                    "Fail: %s." % ("wait_for_network_service failure"),
                     extras={"failure_cause": self.my_error_msg})
             tasks = [(self.verify_device_status, (ad, VOICE_CALL))
-                for ad in self.android_devices]
+                     for ad in self.android_devices]
             asserts.assert_true(multithread_func(self.log, tasks),
-                "Fail: %s." %("verify_device_status failure"),
-                extras={"failure_cause": self.my_error_msg})
+                                "Fail: %s." % ("verify_device_status failure"),
+                                extras={"failure_cause": self.my_error_msg})
         return True
 
     def _dsds_in_out_service_idle_test(self, idle_time=60):
@@ -146,7 +171,6 @@
         '''
         return self._in_out_service_idle(idle_time)
 
-
     def _dsds_in_out_service_in_call_transfer_test(self, idle_time=60):
         '''
             (1) UE is performing data transfer (E.g. Use FTP or browse tools)
@@ -160,8 +184,12 @@
                 True if pass; False if fail
         '''
         error_msg = ""
-        tasks_a = [(active_file_download_test, (self.log, ad, )) for ad in self.android_devices]
-        tasks_b= [(mo_voice_call, (self.log, ad, VOICE_CALL, False)) for ad in self.android_devices]
+        tasks_a = [(active_file_download_test, (
+            self.log,
+            ad,
+        )) for ad in self.android_devices]
+        tasks_b = [(mo_voice_call, (self.log, ad, VOICE_CALL, False))
+                   for ad in self.android_devices]
         tasks_b.extend(tasks_a)
         if not multithread_func(self.log, tasks_b):
             error_msg = "fail to perfrom data transfer/voice call"
@@ -182,7 +210,8 @@
                 True if pass; False if fail
         '''
         error_msg = ""
-        tasks = [(mo_voice_call, (self.log, ad, VOICE_CALL, False)) for ad in self.android_devices]
+        tasks = [(mo_voice_call, (self.log, ad, VOICE_CALL, False))
+                 for ad in self.android_devices]
         if not multithread_func(self.log, tasks):
             error_msg = "MO voice call fail"
             self.my_error_msg += error_msg
@@ -217,7 +246,9 @@
                 return False
         return True
 
-    def _dsds_in_out_service_data_transfer_test(self, idle_time=60, file_name="10MB"):
+    def _dsds_in_out_service_data_transfer_test(self,
+                                                idle_time=60,
+                                                file_name="10MB"):
         '''
             (1) UE is performing data transfer (E.g. Use FTP or browse tools)
             (2) Move UE from coverage area to no service area and UE shows no service
@@ -233,17 +264,17 @@
         '''
         tasks_a = [(self._in_out_service_idle, (idle_time))]
         tasks_b = [(active_file_download_test, (self.log, ad, file_name))
-            for ad in self.android_devices]
+                   for ad in self.android_devices]
         tasks_b.extend(tasks_a)
         if not multithread_func(self.log, tasks_b):
             error_msg = " data transfer fail. "
-            self.my_error_msg +=  error_msg
+            self.my_error_msg += error_msg
             self.log.error(error_msg)
         tasks = [(self.verify_device_status, (ad, VOICE_CALL))
-            for ad in self.android_devices]
-        asserts.assert_true(multithread_func(self.log, tasks), "Fail: %s."
-            %("verify_device_status failure"), extras={"failure_cause":
-            self.my_error_msg})
+                 for ad in self.android_devices]
+        asserts.assert_true(multithread_func(self.log, tasks),
+                            "Fail: %s." % ("verify_device_status failure"),
+                            extras={"failure_cause": self.my_error_msg})
         return True
 
     def _in_out_service_idle(self, idle_time):
@@ -260,11 +291,11 @@
         self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
         return True
 
-
-
     @test_tracker_info(uuid="053465d8-a682-404c-a0fb-8e79f6ca581d")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_idle_msim_4g_esim_4g_dds_sim1_1min(self, loop=50, idle_time=60):
+    def test_in_out_idle_msim_4g_esim_4g_dds_sim1_1min(self,
+                                                       loop=50,
+                                                       idle_time=60):
         '''
             1.8.17 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary idle mode - 1 min
@@ -278,10 +309,11 @@
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
         return self._dsds_in_out_service_test(IDLE_CASE, loop, idle_time, 0)
 
-
     @test_tracker_info(uuid="1ba35ced-41d1-456d-84e2-a40a0d7402b2")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_idle_msim_4g_esim_4g_dds_sim2_1min(self, loop=50, idle_time=60):
+    def test_in_out_idle_msim_4g_esim_4g_dds_sim2_1min(self,
+                                                       loop=50,
+                                                       idle_time=60):
         '''
             1.8.18 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary idle mode - 1 min
@@ -297,8 +329,9 @@
 
     @test_tracker_info(uuid="53697dd9-a2f6-4eb5-8b2c-5c9f2a5417ad")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_idle_msim_4g_esim_4g_dds_sim1_2min(self, loop=1,
-        idle_time=120):
+    def test_in_out_idle_msim_4g_esim_4g_dds_sim1_2min(self,
+                                                       loop=1,
+                                                       idle_time=120):
         '''
             1.8.19 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service
             Stationary idle mode - 2 mins
@@ -314,8 +347,9 @@
 
     @test_tracker_info(uuid="f329bb22-c74f-4688-9983-eaf88131a630")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_idle_msim_4g_esim_4g_dds_sim2_2min(self, loop=1,
-        idle_time=120):
+    def test_in_out_idle_msim_4g_esim_4g_dds_sim2_2min(self,
+                                                       loop=1,
+                                                       idle_time=120):
         '''
             1.8.20 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary idle mode - 2 mins
@@ -331,8 +365,9 @@
 
     @test_tracker_info(uuid="4d8cba59-921b-441c-94dc-8c43a12593ea")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_idle_msim_4g_esim_4g_dds_sim1_5min(self, loop=1,
-        idle_time=300):
+    def test_in_out_idle_msim_4g_esim_4g_dds_sim1_5min(self,
+                                                       loop=1,
+                                                       idle_time=300):
         '''
             1.8.21 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary idle mode - 5 mins
@@ -348,8 +383,9 @@
 
     @test_tracker_info(uuid="dfb3646f-b21f-41f4-a70b-f7ca93ff56ec")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_idle_msim_4g_esim_4g_dds_sim2_5min(self, loop=1,
-        idle_time=300):
+    def test_in_out_idle_msim_4g_esim_4g_dds_sim2_5min(self,
+                                                       loop=1,
+                                                       idle_time=300):
         '''
             1.8.22 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary idle mode - 5 mins
@@ -363,11 +399,10 @@
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
         return self._dsds_in_out_service_test(IDLE_CASE, loop, idle_time, 1)
 
-
     @test_tracker_info(uuid="95e026e1-8f3e-4b9e-8d13-96a2d3be2d23")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_idle_msim_4g_esim_4g_dds_sim1_10min(self, loop=1,
-        idle_time=600):
+    def test_in_out_idle_msim_4g_esim_4g_dds_sim1_10min(
+            self, loop=1, idle_time=600):
         '''
             1.8.23 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary idle mode - 10 mins
@@ -383,8 +418,8 @@
 
     @test_tracker_info(uuid="935ed9be-94ef-4f46-b742-4bfac16b876d")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_idle_msim_4g_esim_4g_dds_sim2_10min(self, loop=1,
-        idle_time=600):
+    def test_in_out_idle_msim_4g_esim_4g_dds_sim2_10min(
+            self, loop=1, idle_time=600):
         '''
             1.8.24 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary idle mode - 10 mins
@@ -398,11 +433,10 @@
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
         return self._dsds_in_out_service_test(IDLE_CASE, loop, idle_time, 1)
 
-
     @test_tracker_info(uuid="919e478e-6ea4-4bdc-b7d8-0252c7fa1510")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_4g_esim_4g_dds_sim1_1min(
-        self, loop=20, idle_time=60):
+            self, loop=20, idle_time=60):
         '''
             1.8.25 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data transfer - 1 min
@@ -414,12 +448,13 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop, idle_time, 0)
+        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop,
+                                              idle_time, 0)
 
     @test_tracker_info(uuid="0826b234-7619-4ad9-b1e9-81d4d7e51be4")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_4g_esim_4g_dds_sim2_1min(
-        self, loop=20, idle_time=60):
+            self, loop=20, idle_time=60):
         '''
             1.8.26 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data transfer - 1 min
@@ -431,13 +466,13 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop, idle_time, 1)
-
+        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop,
+                                              idle_time, 1)
 
     @test_tracker_info(uuid="baf5a72d-2a44-416b-b50d-80a4e6d75373")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_4g_esim_4g_dds_sim1_2min(
-        self, loop=20, idle_time=120):
+            self, loop=20, idle_time=120):
         '''
             1.8.27 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data transfer - 2 mins
@@ -449,13 +484,13 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop, idle_time, 0)
-
+        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop,
+                                              idle_time, 0)
 
     @test_tracker_info(uuid="e74bbe30-6ced-4122-8088-3f7f7bcd35d1")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_4g_esim_4g_dds_sim2_2min(
-        self, loop=20, idle_time=120):
+            self, loop=20, idle_time=120):
         '''
             1.8.28 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data transfer - 2 mins
@@ -467,13 +502,13 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop, idle_time, 1)
-
+        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop,
+                                              idle_time, 1)
 
     @test_tracker_info(uuid="d605bdc1-c262-424b-aa05-dd64db0f150d")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_4g_esim_4g_dds_sim1_5min(
-        self, loop=20, idle_time=300):
+            self, loop=20, idle_time=300):
         '''
             1.8.29 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data transfer - 5 mins
@@ -485,13 +520,13 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop, idle_time, 0)
-
+        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop,
+                                              idle_time, 0)
 
     @test_tracker_info(uuid="590f6292-c19e-44f9-9050-8e4ad6ef0047")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_4g_esim_4g_dds_sim2_5min(
-        self, loop=20, idle_time=300):
+            self, loop=20, idle_time=300):
         '''
             1.8.30 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data transfer - 5 mins
@@ -503,13 +538,13 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop, idle_time, 1)
-
+        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop,
+                                              idle_time, 1)
 
     @test_tracker_info(uuid="6d4c631d-d4b1-4974-bcf5-f63d655a43d8")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_4g_esim_4g_dds_sim1_10min(
-        self, loop=20, idle_time=600):
+            self, loop=20, idle_time=600):
         '''
             1.8.31 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data transfer - 10 mins
@@ -521,12 +556,13 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop, idle_time, 0)
+        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop,
+                                              idle_time, 0)
 
     @test_tracker_info(uuid="ec4c4b08-d306-4d95-af07-485953afe741")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_4g_esim_4g_dds_sim2_10min(
-        self, loop=20, idle_time=600):
+            self, loop=20, idle_time=600):
         '''
             1.8.32 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data transfer - 10 mins
@@ -538,13 +574,13 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop, idle_time, 1)
-
-
+        return self._dsds_in_out_service_test(DATA_TRANSFER_CASE, loop,
+                                              idle_time, 1)
 
     @test_tracker_info(uuid="9a3827bd-132b-42de-968d-802b7e2e22cc")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_data_off_msim_4g_esim_4g_dds_sim1_1min(self, loop=50, idle_time=60):
+    def test_in_out_data_off_msim_4g_esim_4g_dds_sim1_1min(
+            self, loop=50, idle_time=60):
         '''
             1.8.33 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data off - 1 min
@@ -556,11 +592,13 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_OFF_CASE, loop, idle_time, 0)
+        return self._dsds_in_out_service_test(DATA_OFF_CASE, loop, idle_time,
+                                              0)
 
     @test_tracker_info(uuid="4c42e33b-188c-4c62-8def-f47c46a07555")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_data_off_msim_4g_esim_4g_dds_sim1_2min(self, loop=50, idle_time=120):
+    def test_in_out_data_off_msim_4g_esim_4g_dds_sim1_2min(
+            self, loop=50, idle_time=120):
         '''
             1.8.34 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data off - 2 mins
@@ -572,11 +610,13 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_OFF_CASE, loop, idle_time, 0)
+        return self._dsds_in_out_service_test(DATA_OFF_CASE, loop, idle_time,
+                                              0)
 
     @test_tracker_info(uuid="d40ee1cb-0e63-43f4-8b45-6a3a9bc1fcaa")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_data_off_msim_4g_esim_4g_dds_sim1_5min(self, loop=10, idle_time=300):
+    def test_in_out_data_off_msim_4g_esim_4g_dds_sim1_5min(
+            self, loop=10, idle_time=300):
         '''
             1.8.35 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data off - 5 mins
@@ -588,12 +628,15 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_OFF_CASE, loop, idle_time, dds_slot=0)
-
+        return self._dsds_in_out_service_test(DATA_OFF_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=0)
 
     @test_tracker_info(uuid="a0bb09bf-36c2-45cc-91d3-5441fd90a2ee")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_data_off_msim_4g_esim_4g_dds_sim1_10min(self, loop=10, idle_time=600):
+    def test_in_out_data_off_msim_4g_esim_4g_dds_sim1_10min(
+            self, loop=10, idle_time=600):
         '''
             1.8.36 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary data off - 10 mins
@@ -605,12 +648,15 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(DATA_OFF_CASE, loop, idle_time, dds_slot=0)
-
+        return self._dsds_in_out_service_test(DATA_OFF_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=0)
 
     @test_tracker_info(uuid="d267f0bb-427a-4bed-9d78-20dbc193588f")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_in_call_msim_4g_esim_4g_dds_sim1_call_sim1_1min(self, loop=10, idle_time=60):
+    def test_in_out_in_call_msim_4g_esim_4g_dds_sim1_call_sim1_1min(
+            self, loop=10, idle_time=60):
         '''
             1.8.37 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall - 1 min
@@ -622,12 +668,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(IN_CALL_CASE, loop, idle_time, dds_slot=0,
-            voice_slot=0, sms_slot=0)
+        return self._dsds_in_out_service_test(IN_CALL_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=0,
+                                              voice_slot=0,
+                                              sms_slot=0)
 
     @test_tracker_info(uuid="f0fcfc8f-4867-4b3c-94b8-4b406fa4ce8f")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_in_call_msim_4g_esim_4g_dds_sim2_call_sim2_1min(self, loop=10, idle_time=60):
+    def test_in_out_in_call_msim_4g_esim_4g_dds_sim2_call_sim2_1min(
+            self, loop=10, idle_time=60):
         '''
             1.8.38 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall - 1 min
@@ -639,12 +690,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(IN_CALL_CASE, loop, idle_time,
-            dds_slot=1, voice_slot=1, sms_slot=1)
+        return self._dsds_in_out_service_test(IN_CALL_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=1,
+                                              voice_slot=1,
+                                              sms_slot=1)
 
     @test_tracker_info(uuid="5f96c891-fdb3-4367-afba-539eeb57ff0f")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_in_call_msim_4g_esim_4g_dds_sim1_call_sim1_2min(self, loop=10, idle_time=120):
+    def test_in_out_in_call_msim_4g_esim_4g_dds_sim1_call_sim1_2min(
+            self, loop=10, idle_time=120):
         '''
             1.8.39 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall - 2 mins
@@ -656,13 +712,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(IN_CALL_CASE, loop, idle_time,
-            dds_slot=0, voice_slot=0, sms_slot=0)
-
+        return self._dsds_in_out_service_test(IN_CALL_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=0,
+                                              voice_slot=0,
+                                              sms_slot=0)
 
     @test_tracker_info(uuid="3920a8b7-492b-4bc4-9b3d-6d7df9861934")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_in_call_msim_4g_esim_4g_dds_sim2_call_sim2_2min(self, loop=10, idle_time=120):
+    def test_in_out_in_call_msim_4g_esim_4g_dds_sim2_call_sim2_2min(
+            self, loop=10, idle_time=120):
         '''
             1.8.40 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall - 2 mins
@@ -674,13 +734,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(IN_CALL_CASE, loop, idle_time,
-            dds_slot=1, voice_slot=1, sms_slot=1)
-
+        return self._dsds_in_out_service_test(IN_CALL_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=1,
+                                              voice_slot=1,
+                                              sms_slot=1)
 
     @test_tracker_info(uuid="b8fac57b-fdf8-48e6-a51f-349a512d2df7")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_in_call_msim_4g_esim_4g_dds_sim1_call_sim1_5min(self, loop=10, idle_time=300):
+    def test_in_out_in_call_msim_4g_esim_4g_dds_sim1_call_sim1_5min(
+            self, loop=10, idle_time=300):
         '''
             1.8.41 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall - 5 mins
@@ -692,13 +756,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(IN_CALL_CASE, loop, idle_time,
-            dds_slot=0, voice_slot=0, sms_slot=0)
+        return self._dsds_in_out_service_test(IN_CALL_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=0,
+                                              voice_slot=0,
+                                              sms_slot=0)
 
     @test_tracker_info(uuid="0f0f7749-5cf8-4030-aae5-d28cb3a26d9b")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_in_call_msim_4g_esim_4g_dds_sim2_call_sim2_5min(self, loop=10,
-        idle_time=300):
+    def test_in_out_in_call_msim_4g_esim_4g_dds_sim2_call_sim2_5min(
+            self, loop=10, idle_time=300):
         '''
             1.8.42 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall - 5 mins
@@ -710,14 +778,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(IN_CALL_CASE, loop, idle_time,
-            dds_slot=1, voice_slot=1, sms_slot=1)
-
+        return self._dsds_in_out_service_test(IN_CALL_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=1,
+                                              voice_slot=1,
+                                              sms_slot=1)
 
     @test_tracker_info(uuid="53c8bc90-b9a6-46c7-a412-fe5b9f8df3c3")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_in_call_msim_4g_esim_4g_dds_sim1_call_sim1_10min(self, loop=10,
-        idle_time=600):
+    def test_in_out_in_call_msim_4g_esim_4g_dds_sim1_call_sim1_10min(
+            self, loop=10, idle_time=600):
         '''
             1.8.43 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall - 10 mins
@@ -732,13 +803,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(IN_CALL_CASE, loop, idle_time,
-            dds_slot=0, voice_slot=0, sms_slot=0)
+        return self._dsds_in_out_service_test(IN_CALL_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=0,
+                                              voice_slot=0,
+                                              sms_slot=0)
 
     @test_tracker_info(uuid="cff1893e-ea14-4e32-83ae-9116ffd96da4")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_in_call_msim_4g_esim_4g_dds_sim2_call_sim2_10min(self, loop=10,
-        idle_time=600):
+    def test_in_out_in_call_msim_4g_esim_4g_dds_sim2_call_sim2_10min(
+            self, loop=10, idle_time=600):
         '''
             1.8.44 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall - 10 mins
@@ -750,14 +825,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(IN_CALL_CASE, loop, idle_time,
-            dds_slot=1, voice_slot=1, sms_slot=1)
-
+        return self._dsds_in_out_service_test(IN_CALL_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=1,
+                                              voice_slot=1,
+                                              sms_slot=1)
 
     @test_tracker_info(uuid="a73d70d2-d5dd-4901-8cfe-6e54bdd4ddc3")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_call_data_msim_4g_esim_4g_dds_sim1_call_sim1_1min(self, loop=10,
-        idle_time=60):
+    def test_in_out_call_data_msim_4g_esim_4g_dds_sim1_call_sim1_1min(
+            self, loop=10, idle_time=60):
         '''
             1.8.45 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall + data transfer - 1 min
@@ -769,13 +847,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(CALL_DATA_CASE, loop, idle_time,
-            dds_slot=0, voice_slot=0, sms_slot=0)
-
+        return self._dsds_in_out_service_test(CALL_DATA_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=0,
+                                              voice_slot=0,
+                                              sms_slot=0)
 
     @test_tracker_info(uuid="6d331e0e-368d-4752-810c-ad497ccb0001")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_call_data_msim_4g_esim_4g_dds_sim2_call_sim2_1min(self, loop=10, idle_time=60):
+    def test_in_out_call_data_msim_4g_esim_4g_dds_sim2_call_sim2_1min(
+            self, loop=10, idle_time=60):
         '''
             1.8.46 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall + data transfer - 1 min
@@ -787,15 +869,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(CALL_DATA_CASE, loop, idle_time,
-            dds_slot=1, voice_slot=1, sms_slot=1)
-
-
+        return self._dsds_in_out_service_test(CALL_DATA_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=1,
+                                              voice_slot=1,
+                                              sms_slot=1)
 
     @test_tracker_info(uuid="2b4c5912-f654-45ad-8195-284c602c194f")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_call_data_msim_4g_esim_4g_dds_sim1_call_sim1_2min(self, loop=10,
-        idle_time=120):
+    def test_in_out_call_data_msim_4g_esim_4g_dds_sim1_call_sim1_2min(
+            self, loop=10, idle_time=120):
         '''
             1.8.47 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall + data transfer - 2 mins
@@ -807,14 +891,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(CALL_DATA_CASE, loop, idle_time,
-            dds_slot=0, voice_slot=0, sms_slot=0)
-
+        return self._dsds_in_out_service_test(CALL_DATA_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=0,
+                                              voice_slot=0,
+                                              sms_slot=0)
 
     @test_tracker_info(uuid="d0428b18-6c5b-42d9-96fd-423a0512b95e")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_call_data_msim_4g_esim_4g_dds_sim2_call_sim2_2min(self, loop=10,
-        idle_time=120):
+    def test_in_out_call_data_msim_4g_esim_4g_dds_sim2_call_sim2_2min(
+            self, loop=10, idle_time=120):
         '''
             1.8.48 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall + data transfer - 2 mins
@@ -826,15 +913,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(CALL_DATA_CASE, loop, idle_time,
-            dds_slot=1, voice_slot=1, sms_slot=1)
-
-
+        return self._dsds_in_out_service_test(CALL_DATA_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=1,
+                                              voice_slot=1,
+                                              sms_slot=1)
 
     @test_tracker_info(uuid="66b2ec18-d2f8-46b3-8626-0688f4f7c0dc")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_call_data_msim_4g_esim_4g_dds_sim1_call_sim1_5min(self, loop=10,
-        idle_time=300):
+    def test_in_out_call_data_msim_4g_esim_4g_dds_sim1_call_sim1_5min(
+            self, loop=10, idle_time=300):
         '''
             1.8.49 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall + data transfer - 5 mins
@@ -846,14 +935,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(CALL_DATA_CASE, loop, idle_time,
-            dds_slot=0, voice_slot=0, sms_slot=0)
-
+        return self._dsds_in_out_service_test(CALL_DATA_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=0,
+                                              voice_slot=0,
+                                              sms_slot=0)
 
     @test_tracker_info(uuid="4634689f-3ab5-4826-9057-668b0fe15402")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_call_data_msim_4g_esim_4g_dds_sim2_call_sim2_5min(self, loop=10,
-        idle_time=300):
+    def test_in_out_call_data_msim_4g_esim_4g_dds_sim2_call_sim2_5min(
+            self, loop=10, idle_time=300):
         '''
             1.8.50 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall + data transfer - 5 mins
@@ -865,14 +957,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(CALL_DATA_CASE, loop, idle_time,
-            dds_slot=1, voice_slot=1, sms_slot=1)
-
+        return self._dsds_in_out_service_test(CALL_DATA_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=1,
+                                              voice_slot=1,
+                                              sms_slot=1)
 
     @test_tracker_info(uuid="5a097f66-dbd4-49d4-957c-d0e9584de36b")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_call_data_msim_4g_esim_4g_dds_sim1_call_sim1_10min(self, loop=10,
-        idle_time=600):
+    def test_in_out_call_data_msim_4g_esim_4g_dds_sim1_call_sim1_10min(
+            self, loop=10, idle_time=600):
         '''
             1.8.51 - [DDS:SIM1][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall + data transfer - 10 mins
@@ -884,14 +979,17 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(CALL_DATA_CASE, loop, idle_time,
-            dds_slot=0, voice_slot=0, sms_slot=0)
-
+        return self._dsds_in_out_service_test(CALL_DATA_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=0,
+                                              voice_slot=0,
+                                              sms_slot=0)
 
     @test_tracker_info(uuid="d99c0700-27b1-4b0c-881b-ccf908a70287")
     @TelephonyBaseTest.tel_test_wrap
-    def test_in_out_call_data_msim_4g_esim_4g_dds_sim2_call_sim2_10min(self, loop=10,
-        idle_time=600):
+    def test_in_out_call_data_msim_4g_esim_4g_dds_sim2_call_sim2_10min(
+            self, loop=10, idle_time=600):
         '''
             1.8.52 - [DDS:SIM2][SIM1:VoLTE, SIM2:VoLTE] In/Out service -
             Stationary incall + data transfer - 10 mins
@@ -903,5 +1001,9 @@
                 True if pass; False if fail
         '''
         loop = self.user_params.get("4g_dsds_io_cycle", 1)
-        return self._dsds_in_out_service_test(CALL_DATA_CASE, loop, idle_time,
-            dds_slot=1, voice_slot=1, sms_slot=1)
\ No newline at end of file
+        return self._dsds_in_out_service_test(CALL_DATA_CASE,
+                                              loop,
+                                              idle_time,
+                                              dds_slot=1,
+                                              voice_slot=1,
+                                              sms_slot=1)
diff --git a/acts_tests/tests/google/tel/lab/TelLabGFTDSDSTest.py b/acts_tests/tests/google/tel/lab/TelLabGFTDSDSTest.py
index a0f0d3b..8cabbd0 100644
--- a/acts_tests/tests/google/tel/lab/TelLabGFTDSDSTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabGFTDSDSTest.py
@@ -14,9 +14,7 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-
 import time
-import datetime
 from acts import asserts
 from acts.test_decorators import test_tracker_info
 
@@ -34,13 +32,13 @@
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0
 from acts_contrib.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1
 
-
 _5G_VOLTE = "5g_volte"
 _VOLTE = "volte"
 _NO_SERVICE_TIME = 30
 _ERROR_MSG_DATA_TRANSFER_FAILURE = "_test_in_out_service_data_transfer failure"
 _ERROR_MSG_IDLE_FAILURE = "_test_in_out_service_idle failure"
 
+
 class TelLabGFTDSDSTest(GFTInOutBaseTest):
     def __init__(self, controllers):
         # requirs 2 android devices to run DSDS test
@@ -73,14 +71,14 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            asserts.assert_true(
-                self._test_in_out_service_idle(_5G_VOLTE, _5G_VOLTE, 0),
-                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
-                extras={"failure_cause": self.my_error_msg})
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
+            asserts.assert_true(self._test_in_out_service_idle(
+                _5G_VOLTE, _5G_VOLTE, 0),
+                                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
+                                extras={"failure_cause": self.my_error_msg})
         return True
 
-
     @test_tracker_info(uuid="21b3ff34-e42a-4d42-ba98-87c510e83967")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_msim_5g_esim_5g_dds_sim2(self, loop=1):
@@ -103,14 +101,14 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            asserts.assert_true(
-                self._test_in_out_service_idle(_5G_VOLTE, _5G_VOLTE, 1),
-                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
-                extras={"failure_cause": self.my_error_msg})
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
+            asserts.assert_true(self._test_in_out_service_idle(
+                _5G_VOLTE, _5G_VOLTE, 1),
+                                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
+                                extras={"failure_cause": self.my_error_msg})
         return True
 
-
     @test_tracker_info(uuid="f1311823-e6e4-478e-a38d-2344389698b7")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_msim_4g_esim_5g_dds_sim1(self, loop=1):
@@ -133,14 +131,14 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            asserts.assert_true(
-                self._test_in_out_service_idle(_VOLTE, _5G_VOLTE, 0),
-                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
-                extras={"failure_cause": self.my_error_msg})
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
+            asserts.assert_true(self._test_in_out_service_idle(
+                _VOLTE, _5G_VOLTE, 0),
+                                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
+                                extras={"failure_cause": self.my_error_msg})
         return True
 
-
     @test_tracker_info(uuid="7dc38fd5-741f-42b0-a476-3aa51610d184")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_msim_4g_esim_5g_dds_sim2(self, loop=1):
@@ -165,11 +163,12 @@
                 TestFailure if not success.
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            asserts.assert_true(
-                self._test_in_out_service_idle(_VOLTE, _5G_VOLTE, 1),
-                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
-                extras={"failure_cause": self.my_error_msg})
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
+            asserts.assert_true(self._test_in_out_service_idle(
+                _VOLTE, _5G_VOLTE, 1),
+                                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
+                                extras={"failure_cause": self.my_error_msg})
         return True
 
     @test_tracker_info(uuid="a47cdaf6-87b6-416e-a0e4-ebdd2ec5f3f1")
@@ -194,14 +193,14 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            asserts.assert_true(
-                self._test_in_out_service_idle(_5G_VOLTE, _VOLTE, 0),
-                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
-                extras={"failure_cause": self.my_error_msg})
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
+            asserts.assert_true(self._test_in_out_service_idle(
+                _5G_VOLTE, _VOLTE, 0),
+                                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
+                                extras={"failure_cause": self.my_error_msg})
         return True
 
-
     @test_tracker_info(uuid="5e2e3ce2-6d37-48dd-9007-6aa3f593150b")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_msim_5g_esim_4g_dds_sim2(self, loop=1):
@@ -224,14 +223,14 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            asserts.assert_true(
-                self._test_in_out_service_idle(_5G_VOLTE, _VOLTE, 1),
-                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
-                extras={"failure_cause": self.my_error_msg})
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
+            asserts.assert_true(self._test_in_out_service_idle(
+                _5G_VOLTE, _VOLTE, 1),
+                                "[Fail]%s" % (_ERROR_MSG_IDLE_FAILURE),
+                                extras={"failure_cause": self.my_error_msg})
         return True
 
-
     @test_tracker_info(uuid="51f291f0-af5f-400c-9678-4f129695bb68")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_5g_esim_5g_dds_sim1(self, loop=1):
@@ -254,14 +253,15 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            asserts.assert_true(
-                self._test_in_out_service_data_transfer(_5G_VOLTE, _5G_VOLTE, 0),
-                "[Fail]%s" % (_ERROR_MSG_DATA_TRANSFER_FAILURE),
-                extras={"failure_cause": self.my_error_msg})
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
+            asserts.assert_true(self._test_in_out_service_data_transfer(
+                _5G_VOLTE, _5G_VOLTE, 0),
+                                "[Fail]%s" %
+                                (_ERROR_MSG_DATA_TRANSFER_FAILURE),
+                                extras={"failure_cause": self.my_error_msg})
         return True
 
-
     @test_tracker_info(uuid="d0b134c5-380f-4c74-8ab9-8322de1c59e9")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_5g_esim_5g_dds_sim2(self, loop=1):
@@ -284,14 +284,14 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
             asserts.assert_true(
                 self._test_in_out_service_data_transfer(_VOLTE, _5G_VOLTE, 1),
                 "[Fail]%s" % (_ERROR_MSG_DATA_TRANSFER_FAILURE),
                 extras={"failure_cause": self.my_error_msg})
         return True
 
-
     @test_tracker_info(uuid="c28a9ea5-28a8-4d21-ba25-cb38aca30170")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_4g_esim_5g_dds_sim1(self, loop=1):
@@ -314,14 +314,14 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
             asserts.assert_true(
                 self._test_in_out_service_data_transfer(_VOLTE, _5G_VOLTE, 0),
                 "[Fail]%s" % (_ERROR_MSG_DATA_TRANSFER_FAILURE),
                 extras={"failure_cause": self.my_error_msg})
         return True
 
-
     @test_tracker_info(uuid="c28a9ea5-28a8-4d21-ba25-cb38aca30170")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_4g_esim_5g_dds_sim2(self, loop=1):
@@ -344,7 +344,8 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
             asserts.assert_true(
                 self._test_in_out_service_data_transfer(_VOLTE, _5G_VOLTE, 1),
                 "[Fail]%s" % (_ERROR_MSG_DATA_TRANSFER_FAILURE),
@@ -373,14 +374,14 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
             asserts.assert_true(
                 self._test_in_out_service_data_transfer(_5G_VOLTE, _VOLTE, 0),
                 "[Fail]%s" % (_ERROR_MSG_DATA_TRANSFER_FAILURE),
                 extras={"failure_cause": self.my_error_msg})
         return True
 
-
     @test_tracker_info(uuid="43cd405f-d510-4193-9bff-795db12dbb30")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_data_transfer_msim_5g_esim_4g_dds_sim2(self, loop=1):
@@ -403,26 +404,32 @@
                 True if pass; False if fail
         '''
         for x in range(self.user_params.get("dsds_io_cycle", 1)):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
             asserts.assert_true(
                 self._test_in_out_service_data_transfer(_5G_VOLTE, _VOLTE, 1),
                 "[Fail]%s" % (_ERROR_MSG_DATA_TRANSFER_FAILURE),
                 extras={"failure_cause": self.my_error_msg})
         return True
 
-
-    def _test_in_out_service_idle(self, psim_rat=_5G_VOLTE , esim_rat=_5G_VOLTE,
-                                  dds_slot=0, momt_direction="mo"):
+    def _test_in_out_service_idle(self,
+                                  psim_rat=_5G_VOLTE,
+                                  esim_rat=_5G_VOLTE,
+                                  dds_slot=0,
+                                  momt_direction="mo"):
         ad = self.android_devices[0]
         set_dds_on_slot(ad, dds_slot)
         self.adjust_cellular_signal(NO_SERVICE_POWER_LEVEL)
         time.sleep(_NO_SERVICE_TIME)
         self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
-        return self._test_mo_voice_call(psim_rat, esim_rat, dds_slot, momt_direction)
+        return self._test_mo_voice_call(psim_rat, esim_rat, dds_slot,
+                                        momt_direction)
 
-
-    def _test_in_out_service_data_transfer(self, psim_rat=_5G_VOLTE , esim_rat=_5G_VOLTE,
-                                           dds_slot=0, momt_direction="mo"):
+    def _test_in_out_service_data_transfer(self,
+                                           psim_rat=_5G_VOLTE,
+                                           esim_rat=_5G_VOLTE,
+                                           dds_slot=0,
+                                           momt_direction="mo"):
         ad = self.android_devices[0]
         set_dds_on_slot(ad, dds_slot)
         # start streaming
@@ -432,34 +439,36 @@
         self.adjust_cellular_signal(NO_SERVICE_POWER_LEVEL)
         time.sleep(_NO_SERVICE_TIME)
         self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
-        return self._test_mo_voice_call(psim_rat, esim_rat, dds_slot, momt_direction)
+        return self._test_mo_voice_call(psim_rat, esim_rat, dds_slot,
+                                        momt_direction)
 
-    def _test_mo_voice_call(self, psim_rat=_5G_VOLTE , esim_rat=_5G_VOLTE,
-                            dds_slot =0, momt_direction="mo"):
+    def _test_mo_voice_call(self,
+                            psim_rat=_5G_VOLTE,
+                            esim_rat=_5G_VOLTE,
+                            dds_slot=0,
+                            momt_direction="mo"):
         ad = self.android_devices[0]
         # Make a MOMT voice on SIM1
-        test_result = dsds_voice_call_test(
-            self.log,
-            self.tel_logger,
-            self.android_devices,
-            0,
-            None,
-            dds_slot,
-            mo_rat=[psim_rat, esim_rat],
-            call_direction=momt_direction)
+        test_result = dsds_voice_call_test(self.log,
+                                           self.tel_logger,
+                                           self.android_devices,
+                                           0,
+                                           None,
+                                           dds_slot,
+                                           mo_rat=[psim_rat, esim_rat],
+                                           call_direction=momt_direction)
         ensure_phones_idle(self.log, self.android_devices)
         # Make a MOMT voice on SIM2
-        test_result = dsds_voice_call_test(
-            self.log,
-            self.tel_logger,
-            self.android_devices,
-            1,
-            None,
-            dds_slot,
-            mo_rat=[psim_rat, esim_rat],
-            call_direction=momt_direction)
+        test_result = dsds_voice_call_test(self.log,
+                                           self.tel_logger,
+                                           self.android_devices,
+                                           1,
+                                           None,
+                                           dds_slot,
+                                           mo_rat=[psim_rat, esim_rat],
+                                           call_direction=momt_direction)
         # start streaming
         if not start_youtube_video(ad):
             ad.log.warning("Fail to bring up youtube video")
             time.sleep(10)
-        return test_result
\ No newline at end of file
+        return test_result
diff --git a/acts_tests/tests/google/tel/lab/TelLabGFTInOutServiceTest.py b/acts_tests/tests/google/tel/lab/TelLabGFTInOutServiceTest.py
index 9694749..fe6d1f9 100644
--- a/acts_tests/tests/google/tel/lab/TelLabGFTInOutServiceTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabGFTInOutServiceTest.py
@@ -34,6 +34,9 @@
 from acts_contrib.test_utils.tel.tel_data_utils import active_file_download_test
 from acts_contrib.test_utils.tel.tel_test_utils import get_service_state_by_adb
 from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call
+from acts_contrib.test_utils.tel.tel_logging_utils import log_screen_shot
+from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_ims_registered
+from acts_contrib.test_utils.tel.gft_inout_defines import WAIT_FOR_SERVICE_TIME
 
 IDLE_CASE = 1
 DATA_TRANSFER_CASE = 2
@@ -57,7 +60,8 @@
         self.check_network()
         self.my_error_msg = ""
 
-    @test_tracker_info(uuid="c602e556-8273-4c75-b8fa-4d51ba514654")
+    #@test_tracker_info(uuid="c602e556-8273-4c75-b8fa-4d51ba514654")
+    @test_tracker_info(uuid="aebbafc8-8388-47ac-a22a-c82c177b6eb8")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_no_service_idle_1min(self, idle_time=60):
         """ UE is in idle
@@ -70,7 +74,8 @@
         """
         return self._test_in_out_service_idle(idle_time)
 
-    @test_tracker_info(uuid="c602e556-8273-4c75-b8fa-4d51ba514654")
+    #@test_tracker_info(uuid="c602e556-8273-4c75-b8fa-4d51ba514654")
+    @test_tracker_info(uuid="e5c4e835-0bfd-41cd-8e8d-46eafef67f90")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_no_service_idle_2min(self, idle_time=120):
         """ UE is in idle
@@ -82,7 +87,9 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time)
-    @test_tracker_info(uuid="1d437482-caff-4695-9f3f-f3daf6793540")
+
+    #@test_tracker_info(uuid="1d437482-caff-4695-9f3f-f3daf6793540")
+    @test_tracker_info(uuid="3ffc18f7-d56b-4eca-9add-d5c6c1bbff65")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_no_service_idle_5min(self, idle_time=300):
         """ UE is in idle
@@ -95,7 +102,9 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time)
-    @test_tracker_info(uuid="339b4bf5-57a1-48f0-b26a-83a7db21b08b")
+
+    #@test_tracker_info(uuid="339b4bf5-57a1-48f0-b26a-83a7db21b08b")
+    @test_tracker_info(uuid="29ce2e4f-f63e-4350-bb3a-eb3a65c7e924")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_no_service_idle_10min(self, idle_time=600):
         """ UE is in idle
@@ -108,7 +117,9 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time)
-    @test_tracker_info(uuid="65ebac02-8d5a-48c2-bd26-6d931d6048f1")
+
+    #@test_tracker_info(uuid="65ebac02-8d5a-48c2-bd26-6d931d6048f1")
+    @test_tracker_info(uuid="e99d0b85-d8ba-47e0-9361-8bedb38b06ce")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_no_service_data_transfer_1min(self, idle_time=60):
         """ In/Out service - Stationary data transfer - 1 min
@@ -121,7 +132,9 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, DATA_TRANSFER_CASE)
-    @test_tracker_info(uuid="ec3e7de4-bcf6-4a8a-ae04-868bd7925191")
+
+    #@test_tracker_info(uuid="ec3e7de4-bcf6-4a8a-ae04-868bd7925191")
+    @test_tracker_info(uuid="fcdd8a4f-54df-4487-9e30-cbd589bfab5b")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_no_service_data_transfer_2min(self, idle_time=120):
         """ In/Out service - Stationary data transfer - 2 min
@@ -131,7 +144,8 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, DATA_TRANSFER_CASE)
-    @test_tracker_info(uuid="8bd7017d-0a88-4423-a94b-1e37060bba1d")
+    #@test_tracker_info(uuid="8bd7017d-0a88-4423-a94b-1e37060bba1d")
+    @test_tracker_info(uuid="fcdd8a4f-54df-4487-9e30-cbd589bfab5b")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_no_service_data_transfer_5min(self, idle_time=300):
         """ In/Out service - Stationary data transfer - 5 min
@@ -141,7 +155,8 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, DATA_TRANSFER_CASE)
-    @test_tracker_info(uuid="c3b9c52d-41d3-449c-99ff-4bb830ca0219")
+    #@test_tracker_info(uuid="c3b9c52d-41d3-449c-99ff-4bb830ca0219")
+    @test_tracker_info(uuid="4684282a-e16f-42d8-bbb3-da4f6348f720")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_no_service_data_transfer_10min(self, idle_time=600):
         """ In/Out service - Stationary data transfer - 10 min
@@ -152,7 +167,8 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, DATA_TRANSFER_CASE)
-    @test_tracker_info(uuid="86a6b3b3-e754-4bde-b418-d4273b1ad907")
+    #@test_tracker_info(uuid="86a6b3b3-e754-4bde-b418-d4273b1ad907")
+    @test_tracker_info(uuid="1667cf8f-ad33-45a1-b4b9-256dd5054635")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_incall_1min(self, idle_time=60):
         """ In/Out service - Stationary incall - 1 min
@@ -162,7 +178,8 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, IN_CALL_CASE)
-    @test_tracker_info(uuid="0f8772cd-6f86-48eb-b583-4cbaf80a21a9")
+    #@test_tracker_info(uuid="0f8772cd-6f86-48eb-b583-4cbaf80a21a9")
+    @test_tracker_info(uuid="b6d71f75-d0a8-4010-b8eb-46083469d089")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_incall_2min(self, idle_time=120):
         """ In/Out service - Stationary incall - 2 min
@@ -172,7 +189,8 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, IN_CALL_CASE)
-    @test_tracker_info(uuid="11f24c0f-db33-4eb3-b847-9aed447eb820")
+    #@test_tracker_info(uuid="11f24c0f-db33-4eb3-b847-9aed447eb820")
+    @test_tracker_info(uuid="eb7a3821-dbb2-4e24-9649-fc8ca99afcc0")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_incall_5min(self, idle_time=300):
         """ In/Out service - Stationary incall - 5 min
@@ -182,7 +200,8 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, IN_CALL_CASE)
-    @test_tracker_info(uuid="e318921b-de6b-428b-b2c4-3db7786d7558")
+    #@test_tracker_info(uuid="e318921b-de6b-428b-b2c4-3db7786d7558")
+    @test_tracker_info(uuid="cc8ca8f3-7d93-4a61-baba-c503aba322f9")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_incall_10min(self, idle_time=600):
         """ In/Out service - Stationary incall - 10 min
@@ -192,7 +211,8 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, IN_CALL_CASE)
-    @test_tracker_info(uuid="f6cf0019-e123-4ebd-990b-0fa5b236840c")
+    #@test_tracker_info(uuid="f6cf0019-e123-4ebd-990b-0fa5b236840c")
+    @test_tracker_info(uuid="5c3ea073-58f1-4af1-a196-c3cd507cbe62")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_call_date_1min(self, idle_time=60):
         """ In/Out service - Stationary incall + data transfer - 1 mins
@@ -202,7 +222,8 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, CALL_DATA_CASE)
-    @test_tracker_info(uuid="2f49a9de-0383-4ec6-a8ee-c62f52ea0cf2")
+    #@test_tracker_info(uuid="2f49a9de-0383-4ec6-a8ee-c62f52ea0cf2")
+    @test_tracker_info(uuid="7f25090f-e340-413d-8a6a-0a50d38c8dad")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_call_date_2min(self, idle_time=120):
         """ In/Out service - Stationary incall + data transfer - 2 mins
@@ -212,7 +233,9 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, CALL_DATA_CASE)
-    @test_tracker_info(uuid="73a6eedb-791f-4486-b815-8067a95efd5c")
+
+    #@test_tracker_info(uuid="73a6eedb-791f-4486-b815-8067a95efd5c")
+    @test_tracker_info(uuid="f72f2f46-4e21-4fc2-add5-c3d4424ecd35")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_call_date_5min(self, idle_time=300):
         """ In/Out service - Stationary incall + data transfer - 5 mins
@@ -222,7 +245,9 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, CALL_DATA_CASE)
-    @test_tracker_info(uuid="5cfbc90a-97e1-43e9-a69e-4ce2815c544d")
+
+    #@test_tracker_info(uuid="5cfbc90a-97e1-43e9-a69e-4ce2815c544d")
+    @test_tracker_info(uuid="b57bd9fc-8d7f-41d8-9bb5-f7ac39860ede")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_call_date_10min(self, idle_time=600):
         """ In/Out service - Stationary incall + data transfer - 10 mins
@@ -234,7 +259,8 @@
         return self._test_in_out_service_idle(idle_time, CALL_DATA_CASE)
 
 
-    @test_tracker_info(uuid="c70180c9-5a36-4dc5-9ccc-3e6c0b5e6d37")
+    #@test_tracker_info(uuid="c70180c9-5a36-4dc5-9ccc-3e6c0b5e6d37")
+    @test_tracker_info(uuid="ea27ee5d-1bd7-4b24-a044-1201ac4cde2f")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_pdp_off_1min(self, idle_time=60):
         """ In/Out service - Stationary data off - 1 min
@@ -248,7 +274,8 @@
         """
         return self._test_in_out_service_idle(idle_time, PDP_OFF_CASE)
 
-    @test_tracker_info(uuid="50cc8e73-d96f-45a6-91cd-bf51de5241d2")
+    #@test_tracker_info(uuid="50cc8e73-d96f-45a6-91cd-bf51de5241d2")
+    @test_tracker_info(uuid="904ff9c7-5566-4f7b-9168-a73f9ee5c967")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_pdp_off_2min(self, idle_time=120):
         """ In/Out service - Stationary data off - 2 min
@@ -258,7 +285,9 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, PDP_OFF_CASE)
-    @test_tracker_info(uuid="1f25d40c-1bfe-4d18-b57c-d7be69664f0d")
+
+    #@test_tracker_info(uuid="1f25d40c-1bfe-4d18-b57c-d7be69664f0d")
+    @test_tracker_info(uuid="b4b271e3-90f7-4ffb-8938-5df5791dfc0d")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_pdp_off_5min(self, idle_time=300):
         """ In/Out service - Stationary data off - 5 min
@@ -268,7 +297,9 @@
                 True if pass; False if fail.
         """
         return self._test_in_out_service_idle(idle_time, PDP_OFF_CASE)
-    @test_tracker_info(uuid="b076b0d0-a105-4be9-aa0b-db0d782f70f2")
+
+    #@test_tracker_info(uuid="b076b0d0-a105-4be9-aa0b-db0d782f70f2")
+    @test_tracker_info(uuid="fadc53a3-429f-48fc-bd84-7f795a82797a")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_service_pdp_off_10min(self, idle_time=600):
         """ In/Out service - Stationary data off - 10 min
@@ -292,8 +323,8 @@
         test_result = True
         if 'autoio_cycle' in self.user_params:
             loop = self.user_params.get('autoio_cycle')
-        for x in range (loop):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name,x+1, loop))
+        for attempt in range(1, loop + 1):
+            self.log.info(f"{self.current_test_name} loop: {attempt}/{loop}")
             if case == IDLE_CASE:
                 if not self._in_out_service_idle_only(idle_time):
                     test_result = False
@@ -311,6 +342,18 @@
                     test_result = False
             asserts.assert_true(test_result, "Fail: %s." %(self.my_error_msg),
                 extras={"failure_cause": self.my_error_msg})
+            tasks = [(wait_for_ims_registered, (self.log, ad, )) for ad in self.android_devices]
+            if not multithread_func(self.log, tasks):
+                tasks = [(check_ims_state, (ad, )) for ad in self.android_devices]
+                if not multithread_func(self.log, tasks):
+                    test_result = False
+                    self._on_failure("ims is not register, ")
+            time.sleep(WAIT_FOR_SERVICE_TIME)
+            tasks = [(self.verify_device_status, (ad, VOICE_CALL))
+                for ad in self.android_devices]
+            test_result = multithread_func(self.log, tasks)
+            asserts.assert_true(test_result, "Fail: %s." %("verify_device_status failure"),
+                extras={"failure_cause": self.my_error_msg})
         return test_result
 
     def _in_out_service_idle_only(self, no_service_time=60, check_back_to_service=True,
@@ -484,20 +527,19 @@
 
         for x in range (loop):
             self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            self.my_error_msg += "cylce%s: " %(x+1)
+            self.my_error_msg += "cycle%s: " %(x+1)
             self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
 
             self.log.info("Turn on IMS")
             tasks = [(toggle_volte, (self.log, ad, True)) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("fail to toggle volte, ")
+                self._on_failure("fail to toggle volte, ")
                 return False
 
-            tasks = [(check_ims_state, (ad, )) for ad in self.android_devices]
+            tasks = [(wait_for_ims_registered, (self.log, ad, )) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("ims is not register, ")
+                self._on_failure("ims is not register, ")
                 return False
-
             self.log.info("Move to no service area")
             self.adjust_cellular_signal(NO_SERVICE_POWER_LEVEL)
             time.sleep(60)
@@ -505,7 +547,7 @@
             self.log.info("Turn off IMS")
             tasks = [(toggle_volte, (self.log, ad, False)) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("fail to toggle volte, ")
+                self._on_failure("fail to toggle volte, ")
                 return False
             self.log.info("Move back to service area and verify device status")
             self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
@@ -513,7 +555,7 @@
                 for ad in self.android_devices]
             test_result = multithread_func(self.log, tasks)
             if not test_result:
-                self._on_fail("verify_device_status fail, ")
+                self._on_failure("verify_device_status fail, ")
                 return False
         if not test_result:
             asserts.assert_true(test_result, "[Fail]%s" %(error_msg),
@@ -537,11 +579,11 @@
         error_msg = ""
         test_result = True
         if 'ims_cycle' in self.user_params:
-            loop = self.user_params.get('ims_cylce')
+            loop = self.user_params.get('ims_cycle')
 
         for x in range (loop):
             self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            self.my_error_msg += "cylce%s: " %(x+1)
+            self.my_error_msg += "cycle%s: " %(x+1)
             self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
 
             tasks = [(check_ims_state, (ad, )) for ad in self.android_devices]
@@ -550,12 +592,12 @@
             self.log.info("Turn on IMS")
             tasks = [(toggle_volte, (self.log, ad, True)) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("fail to toggle volte, ")
+                self._on_failure("fail to toggle volte, ")
                 return False
 
             tasks = [(check_ims_state, (ad, )) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("ims is not register, ")
+                self._on_failure("ims is not register, ")
                 return False
 
             self.log.info("Move to no service area")
@@ -567,13 +609,13 @@
             self.log.info("Turn off IMS")
             tasks = [(toggle_volte, (self.log, ad, False)) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("fail to toggle volte, ")
+                self._on_failure("fail to toggle volte, ")
                 return False
             tasks = [(self.verify_device_status, (ad, VOICE_CALL))
                 for ad in self.android_devices]
             test_result = multithread_func(self.log, tasks)
             if not test_result:
-                self._on_fail( "verify_device_status fail, ")
+                self._on_failure( "verify_device_status fail, ")
                 return False
         return test_result
 
@@ -593,11 +635,11 @@
         error_msg = ""
         test_result = True
         if 'ims_cycle' in self.user_params:
-            loop = self.user_params.get('ims_cylce')
+            loop = self.user_params.get('ims_cycle')
 
         for x in range (loop):
             self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            self.my_error_msg += "cylce%s: " %(x+1)
+            self.my_error_msg += "cycle%s: " %(x+1)
             self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
 
             tasks = [(check_ims_state, (ad, )) for ad in self.android_devices]
@@ -606,19 +648,19 @@
             self.log.info("Turn off IMS")
             tasks = [(toggle_volte, (self.log, ad, False)) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("fail to toggle volte, ")
+                self._on_failure("fail to toggle volte, ")
                 return False
 
             tasks = [(check_ims_state, (ad, )) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("ims is not register, ")
+                self._on_failure("ims is not register, ")
                 return False
 
             self.log.info("CSFB call in service area")
             tasks = [(mo_voice_call, (self.log, ad, CSFB_CALL, True, 30))
                 for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("csfb_call_fail, ")
+                self._on_failure("csfb_call_fail, ")
                 return False
 
             self.log.info("Move to no service area then turn on IMS")
@@ -626,7 +668,7 @@
             time.sleep(60)
             tasks = [(toggle_volte, (self.log, ad, True)) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("fail to toggle volte, ")
+                self._on_failure("fail to toggle volte, ")
                 return False
 
             self.log.info("Move back to service area and verify device status, VOLTE call")
@@ -635,7 +677,7 @@
                 for ad in self.android_devices]
             test_result = multithread_func(self.log, tasks)
             if not test_result:
-                self._on_fail( "verify_device_status fail, ")
+                self._on_failure( "verify_device_status fail, ")
                 return False
         return test_result
 
@@ -658,11 +700,11 @@
         error_msg = ""
         test_result = True
         if 'ims_cycle' in self.user_params:
-            loop = self.user_params.get('ims_cylce')
+            loop = self.user_params.get('ims_cycle')
 
         for x in range (loop):
             self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            self.my_error_msg += "cylce%s: " %(x+1)
+            self.my_error_msg += "cycle%s: " %(x+1)
             self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
 
             tasks = [(check_ims_state, (ad, )) for ad in self.android_devices]
@@ -671,19 +713,19 @@
             self.log.info("Turn off IMS")
             tasks = [(toggle_volte, (self.log, ad, False)) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("fail to toggle volte ")
+                self._on_failure("fail to toggle volte ")
                 return False
 
             tasks = [(check_ims_state, (ad, )) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("ims is not register, ")
+                self._on_failure("ims is not register, ")
                 return False
 
             self.log.info("CSFB call in service area")
-            tasks = [(mo_voice_call, (self.log, ad, CSFB_CALL, true, 30))
+            tasks = [(mo_voice_call, (self.log, ad, CSFB_CALL, True, 30))
                 for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("csfb_call_fail, ")
+                self._on_failure("csfb_call_fail, ")
                 return False
 
             self.log.info("Move to no service area")
@@ -694,7 +736,7 @@
             self.log.info("Turn on ims")
             tasks = [(toggle_volte, (self.log, ad, True)) for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
-                self._on_fail("fail to toggle volte ")
+                self._on_failure("fail to toggle volte ")
                 return False
             self.log.info("Verify device status, VOLTE call")
             self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
@@ -702,7 +744,7 @@
                 for ad in self.android_devices]
             test_result = multithread_func(self.log, tasks)
             if not test_result:
-                self._on_fail("verify_device_status fail, ")
+                self._on_failure("verify_device_status fail, ")
                 return False
         return test_result
 
@@ -726,7 +768,7 @@
 
         for x in range (loop):
             self.log.info("%s loop: %s/%s" %(self.current_test_name, x+1, loop))
-            self.my_error_msg += "cylce%s: " %(x+1)
+            self.my_error_msg += "cycle%s: " %(x+1)
             self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
             for ad in self.android_devices:
                 ad.log.info("initiate voice call to %s " %(ad.mt_phone_number))
@@ -737,6 +779,16 @@
             tasks = [(hangup_call, (self.log, ad)) for ad in self.android_devices]
             multithread_func(self.log, tasks)
             self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
-            if not self._check_after_no_service():
-                return False
+            tasks = [(wait_for_ims_registered, (self.log, ad, )) for ad in self.android_devices]
+            if not multithread_func(self.log, tasks):
+                tasks = [(check_ims_state, (ad, )) for ad in self.android_devices]
+                if not multithread_func(self.log, tasks):
+                    test_result = False
+                    self._on_failure("ims is not register, ")
+            time.sleep(WAIT_FOR_SERVICE_TIME)
+            tasks = [(self.verify_device_status, (ad, VOICE_CALL))
+                for ad in self.android_devices]
+            test_result = multithread_func(self.log, tasks)
+            asserts.assert_true(test_result, "Fail: %s." %("verify_device_status failure"),
+                extras={"failure_cause": self.my_error_msg})
         return test_result
diff --git a/acts_tests/tests/google/tel/lab/TelLabGFTModemConnectivityHelperTest.py b/acts_tests/tests/google/tel/lab/TelLabGFTModemConnectivityHelperTest.py
index 8c51b34..0ef884f 100644
--- a/acts_tests/tests/google/tel/lab/TelLabGFTModemConnectivityHelperTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabGFTModemConnectivityHelperTest.py
@@ -14,19 +14,7 @@
 #   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 json
-import subprocess
-import math
-import re
 
 from acts import asserts
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
@@ -62,8 +50,8 @@
         GFTInOutBaseTest.setup_test(self)
         self.check_network()
 
-
     """Tests"""
+
     @test_tracker_info(uuid="c602e556-8273-4c75-b8fa-4d51ba514654")
     @TelephonyBaseTest.tel_test_wrap
     def test_in_out_no_service(self):
@@ -82,10 +70,9 @@
         self.adjust_wifi_signal(IN_SERVICE_POWER_LEVEL)
         time.sleep(WAIT_FOR_SERVICE_TIME)
         if not self.check_network():
-          return False
+            return False
         return True
 
-
     @test_tracker_info(uuid="e58c7347-a930-4a3a-a740-a6d1cfb19ca0")
     @TelephonyBaseTest.tel_test_wrap
     def test_volte_call_cellular(self, loop=1):
@@ -99,27 +86,35 @@
                 True if pass; False if fail.
         """
         test_result = True
-        for x in range (loop):
-            self.log.info("%s loop: %s/%s" %(self.current_test_name,x+1, loop))
+        for x in range(loop):
+            self.log.info("%s loop: %s/%s" %
+                          (self.current_test_name, x + 1, loop))
             self.adjust_cellular_signal(IN_SERVICE_POWER_LEVEL)
             self.adjust_wifi_signal(IN_SERVICE_POWER_LEVEL)
 
             # Make a MO VoLTE call in service area.
-            tasks = [(self._initiate_call, (ad, )) for ad in self.android_devices]
+            tasks = [(self._initiate_call, (ad, ))
+                     for ad in self.android_devices]
             if not multithread_func(self.log, tasks):
                 return False
-            tasks = [(is_phone_in_call_volte, (ad.log, ad, )) for ad in self.android_devices]
+            tasks = [(is_phone_in_call_volte, (
+                ad.log,
+                ad,
+            )) for ad in self.android_devices]
             test_result = multithread_func(self.log, tasks)
 
             # Move to poor service -> no service area
             self.adjust_atten_slowly(10, NO_SERVICE_AREA)
             time.sleep(NO_SERVICE_TIME)
             # check call status
-            tasks = [(is_phone_in_call_volte, (ad.log, ad, )) for ad in self.android_devices]
+            tasks = [(is_phone_in_call_volte, (
+                ad.log,
+                ad,
+            )) for ad in self.android_devices]
             test_result = multithread_func(self.log, tasks)
             for ad in self.android_devices:
                 telecom_state = ad.droid.telecomGetCallState()
-                ad.log.info("telecom_state %s" %(telecom_state))
+                ad.log.info("telecom_state %s" % (telecom_state))
             # Move back to service area
             self.adjust_atten_slowly(10, IN_SERVICE_AREA)
             time.sleep(WAIT_FOR_SERVICE_TIME)
@@ -135,10 +130,10 @@
             return True if call initiate successfully
         """
         mt = ad.mt_phone_number
-        ad.log.info("set mt number to %s" %(mt))
+        ad.log.info("set mt number to %s" % (mt))
         ad.log.info("check network status before initiate call")
         self.check_network()
-        ad.log.info("_initiate_call to %s" %(mt))
+        ad.log.info("_initiate_call to %s" % (mt))
         if not initiate_call(self.log, ad, mt):
             return False
         return True
diff --git a/acts_tests/tests/google/tel/lab/TelLabGFTVoWifiTest.py b/acts_tests/tests/google/tel/lab/TelLabGFTVoWifiTest.py
index 35305a0..21ea39b 100644
--- a/acts_tests/tests/google/tel/lab/TelLabGFTVoWifiTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabGFTVoWifiTest.py
@@ -832,9 +832,9 @@
             self.adjust_wifi_signal(IN_SERVICE_POWER_LEVEL)
             self.check_network()
 
-            if not self._enable_wifi_calling(wfc_mode, call_type=WFC_CALL,
+            if not self._enable_wifi_calling(wfc_mode, call_type=VOLTE_CALL,
                 end_call=False):
-                raise signals.TestFailure("VoWiFi call failure: %s"
+                raise signals.TestFailure("VoLTE call failure: %s"
                     %(self.my_error_msg))
             self.log.info(" Move to no service area for 1 minute during incall.")
             self.adjust_cellular_signal(NO_SERVICE_POWER_LEVEL)
@@ -1087,7 +1087,7 @@
         return True
 
 
-    @test_tracker_info(uuid="fb431706-737d-4020-b3d1-347dc4d7ce03")
+    @test_tracker_info(uuid="9b586a06-3b33-41be-ae0f-aacf157212b9")
     @TelephonyBaseTest.tel_test_wrap
     def test_wifi_rove_out_wfc(self,loop=1, wfc_mode=WFC_MODE_WIFI_PREFERRED, idle_time=180):
         '''
diff --git a/acts_tests/tests/google/tel/lab/TelLabProjectFiTest.py b/acts_tests/tests/google/tel/lab/TelLabProjectFiTest.py
index c151811..a7750b4 100644
--- a/acts_tests/tests/google/tel/lab/TelLabProjectFiTest.py
+++ b/acts_tests/tests/google/tel/lab/TelLabProjectFiTest.py
@@ -148,10 +148,7 @@
         self.anritsu.disconnect()
         return True
 
-    def _bring_up_callbox(
-            self,
-            set_simulation_func,
-            rat):
+    def _bring_up_callbox(self, set_simulation_func, rat):
         try:
             [self.bts1] = set_simulation_func(self.anritsu, self.user_params,
                                               self.ad.sim_card)
@@ -169,8 +166,7 @@
                 return False
 
             if not ensure_preferred_network_type_for_subscription(
-                    self.ad,
-                    preferred_network_setting):
+                    self.ad, preferred_network_setting):
                 self.log.error(
                     "Failed to set rat family {}, preferred network:{}".format(
                         rat_family, preferred_network_setting))
@@ -454,7 +450,6 @@
 
         if self.pre_switching_callbox_setup():
             self.log.info("Turned ON Anritsu for Switching")
-            pass
         else:
             self.log.error("Failed to Camp to Anritsu")
             return False
diff --git a/acts_tests/tests/google/tel/live/TelLiveDataTest.py b/acts_tests/tests/google/tel/live/TelLiveDataTest.py
index b5c8d69..0442aa6 100755
--- a/acts_tests/tests/google/tel/live/TelLiveDataTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveDataTest.py
@@ -472,7 +472,7 @@
 
     @test_tracker_info(uuid="dcb9bdc6-dbe2-47e1-9c2d-6f37c529d366")
     @TelephonyBaseTest.tel_test_wrap
-    def test_2g(self):
+    def test_2g_data_connectivity(self):
         """Test data connection in 2G.
 
         Turn off airplane mode, disable WiFi, enable Cellular Data.
@@ -517,7 +517,7 @@
 
     @test_tracker_info(uuid="97067ebb-130a-4fcb-8e6b-f4ec5874828f")
     @TelephonyBaseTest.tel_test_wrap
-    def test_3g(self):
+    def test_3g_data_connectivity(self):
         """Test data connection in 3G.
 
         Turn off airplane mode, disable WiFi, enable Cellular Data.
@@ -562,7 +562,7 @@
 
     @test_tracker_info(uuid="9c2f459f-1aac-4c68-818b-8698e8124c8b")
     @TelephonyBaseTest.tel_test_wrap
-    def test_4g(self):
+    def test_4g_data_connectivity(self):
         """Test data connection in 4g.
 
         Turn off airplane mode, disable WiFi, enable Cellular Data.
@@ -607,7 +607,7 @@
 
     @test_tracker_info(uuid="44f47b64-f8bc-4a17-9195-42dcca0806bb")
     @TelephonyBaseTest.tel_test_wrap
-    def test_3g_stress(self):
+    def test_3g_data_connectivity_stress(self):
         """Stress Test data connection in 3G.
 
         This is a stress test for "test_3g".
@@ -649,7 +649,7 @@
 
     @test_tracker_info(uuid="c8876388-0441-4a51-81e6-ac2cb358a531")
     @TelephonyBaseTest.tel_test_wrap
-    def test_4g_stress(self):
+    def test_4g_data_connectivity_stress(self):
         """Stress Test data connection in 4g.
 
         This is a stress test for "test_4g".
diff --git a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py
index b762af0..4bb393e 100644
--- a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py
@@ -20,6 +20,10 @@
 from acts.test_decorators import test_tracker_info
 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 SimSlotInfo
+from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_dds_swap_call_streaming_test
+from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_dds_swap_message_streaming_test
+from acts_contrib.test_utils.tel.tel_defines import YOUTUBE_PACKAGE_NAME
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE
 from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
@@ -60,6 +64,8 @@
 from acts.utils import rand_ascii_str
 
 CallResult = TelephonyVoiceTestResult.CallResult.Value
+_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL = 60
+_WAIT_TIME_FOR_MEP_ENABLE = 180
 
 
 class TelLiveGFTDSDSDDSSwitchTest(TelephonyBaseTest):
@@ -67,8 +73,22 @@
         TelephonyBaseTest.setup_class(self)
         self.message_lengths = (50, 160, 180)
         self.tel_logger = TelephonyMetricLogger.for_test_case()
+        if getattr(self.android_devices[0], 'mep', False):
+            start_time = time.monotonic()
+            timeout = start_time + _WAIT_TIME_FOR_MEP_ENABLE
+            while time.monotonic() < timeout:
+                mep_logs = self.android_devices[0].search_logcat(
+                    "UNSOL_SIM_SLOT_STATUS_CHANGED")
+                if mep_logs:
+                    for mep_log in mep_logs:
+                        if "num_ports=2" in mep_log["log_message"]:
+                            break
+                time.sleep(_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL)
+            else:
+                self.log.warning("Couldn't found MEP enabled logs.")
 
     def teardown_test(self):
+        self.android_devices[0].force_stop_apk(YOUTUBE_PACKAGE_NAME)
         ensure_phones_idle(self.log, self.android_devices)
 
     def _msim_message_test(
@@ -2293,4 +2313,353 @@
             enable_volte=[False, False],
             enable_wfc=[True, True],
             wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED],
-            is_airplane_mode=False)
\ No newline at end of file
+            is_airplane_mode=False)
+
+    # e+e call
+    @test_tracker_info(uuid="ee94a2f1-9aac-4698-adc7-e5525541fac0")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_youtube_esim_port_0_volte_esim_port_1_volte(self):
+        """ LTE DDS swap call test(Initial DDS is on esim port 0).
+
+        1. Check HTTP connection when DDS is on esim port 0 and idle.
+        2. Switch DDS to esim port 1.
+        3. Check HTTP connection when DDS is on esim port 1 and idle.
+        4. Switch DDS to esim port 0, make sure data works fine.
+        """
+        return dsds_dds_swap_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot = [SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            init_dds=1,
+            test_slot=[None, None, None])
+
+    @test_tracker_info(uuid="d74b7a10-b944-4b6b-8271-420beae810f4")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_voice_esim_port_0_mo_volte_esim_port_1_volte(self):
+        """ LTE DDS swap call test(Initial DDS is on esim port 0).
+
+        1. Make MO call via esim port 0 when DDS is on esim port 0 and idle.
+        2. Switch DDS to esim port 1.
+        3. Make MO call via esim port 0 when DDS is on esim port 1 and idle.
+        4. Switch DDS to esim port 0, make sure data works fine.
+
+        After call end will check the dds slot if is attach to the network
+        with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot = [SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            init_dds=1,
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            direction="mo",
+            duration=30,
+            streaming=False)
+
+    @test_tracker_info(uuid="b424f8de-591e-4a72-ba4e-5c0771e9629d")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_voice_esim_port_0_mt_volte_esim_port_1_volte(self):
+        """ LTE DDS swap call test(Initial DDS is on esim port 0).
+
+        1. Receive MT call via esim port 0 when DDS is on esim port 0 and idle.
+        2. Switch DDS to esim port 1.
+        3. Receive MT call via esim port 0 when DDS is on esim port 1 and idle.
+        4. Switch DDS to esim port 0, make sure data works fine.
+
+        After call end will check the dds slot if is attach to the network
+        with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot = [SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            init_dds=1,
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            direction="mt",
+            duration=30,
+            streaming=False)
+
+    @test_tracker_info(uuid="55b0f7ce-351f-4a46-a0cb-bf54a2404edb")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_voice_esim_port_1_mo_volte_esim_port_0_volte(self):
+        """ LTE DDS swap call test(Initial DDS is on esim port 0).
+
+        1. Make MO call via esim port 1 when DDS is on esim port 0 and idle.
+        2. Switch DDS to esim port 1.
+        3. Make MO call via esim port 1 when DDS is on esim port 1 and idle.
+        4. Switch DDS to esim port 0, make sure data works fine.
+
+        After call end will check the dds slot if is attach to the network
+        with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot = [SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            init_dds=1,
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            direction="mo",
+            duration=30,
+            streaming=False)
+
+    @test_tracker_info(uuid="27ecd41f-9e08-44ce-9d5d-95158cb7a354")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_voice_esim_port_1_mt_volte_esim_port_0_volte(self):
+        """ LTE DDS swap call test(Initial DDS is on esim port 0).
+
+        1. Receive MT call via esim port 1 when DDS is on esim port 0 and idle.
+        2. Switch DDS to esim port 1.
+        3. Receive MT call via esim port 1 when DDS is on esim port 1 and idle.
+        4. Switch DDS to esim port 0, make sure data works fine.
+
+        After call end will check the dds slot if is attach to the network
+        with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot = [SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            init_dds=1,
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            direction="mt",
+            duration=30,
+            streaming=False)
+
+    # e+e message
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_sms_esim_port_0_mo_volte_esim_port_1_volte(self):
+        """ LTE DDS swap SMS test(Initial DDS is on esim_port_0).
+
+        1. Make MO SMS via esim_port_0 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MO SMS via esim_port_0 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make SMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            init_dds=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_sms_esim_port_0_mt_volte_esim_port_1_volte(self):
+        """ LTE DDS swap SMS test(Initial DDS is on esim_port_0).
+
+        1. Make MT SMS via esim_port_0 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MT SMS via esim_port_0 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Receive SMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            init_dds=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_sms_esim_port_1_mo_volte_esim_port_0_volte(self):
+        """ LTE DDS swap SMS test(Initial DDS is on esim_port_0).
+
+        1. Make MO SMS via esim_port_1 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MO SMS via esim_port_1 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make SMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            init_dds=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_sms_esim_port_1_mt_volte_esim_port_0_volte(self):
+        """ LTE DDS swap SMS test(Initial DDS is on esim_port_0).
+
+        1. Make MT SMS via esim_port_1 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MT SMS via esim_port_1 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make SMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            init_dds=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_mms_esim_port_0_mo_volte_esim_port_1_volte(self):
+        """ LTE DDS swap MMS test(Initial DDS is on esim_port_0).
+
+        1. Make MO MMS via esim_port_0 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MO MMS via esim_port_0 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make MMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            init_dds=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_mms_esim_port_0_mt_volte_esim_port_1_volte(self):
+        """ LTE DDS swap MMS test(Initial DDS is on esim_port_0).
+
+        1. Make MT MMS via esim_port_0 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MT MMS via esim_port_0 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Receive MMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1,
+                SimSlotInfo.SLOT_1],
+            init_dds=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_mms_esim_port_1_mo_volte_esim_port_0_volte(self):
+        """ LTE DDS swap MMS test(Initial DDS is on esim_port_0).
+
+        1. Make MO MMS via esim_port_1 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MO MMS via esim_port_1 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make MMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            init_dds=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_dds_switch_mms_esim_port_1_mt_volte_esim_port_0_volte(self):
+        """ LTE DDS swap MMS test(Initial DDS is on esim_port_0).
+
+        1. Make MT MMS via esim_port_1 when DDS is on esim_port_0 and idle.
+        2. Switch DDS to esim_port_1.
+        3. Make MT MMS via esim_port_1 when DDS is on esim_port_1 and idle.
+        4. Switch DDS to esim_port_0, make sure data works fine.
+
+        After Make MMS will check the dds slot if is attach to the
+        network with assigned RAT successfully and data works fine.
+        """
+        return dsds_dds_swap_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=[
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2,
+                SimSlotInfo.SLOT_2],
+            init_dds=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
\ No newline at end of file
diff --git a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSMessageTest.py b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSMessageTest.py
index 344da22..04bff2f 100644
--- a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSMessageTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSMessageTest.py
@@ -14,12 +14,16 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import time
+
 from acts import signals
 from acts.test_decorators import test_tracker_info
 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 SimSlotInfo
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
+from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_message_streaming_test
 from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_message_test
 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 phone_setup_on_rat
@@ -40,6 +44,10 @@
 from acts.utils import rand_ascii_str
 from acts.libs.utils.multithread import multithread_func
 
+_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL = 60
+_WAIT_TIME_FOR_MEP_ENABLE = 180
+
+
 CallResult = TelephonyVoiceTestResult.CallResult.Value
 
 class TelLiveGFTDSDSMessageTest(TelephonyBaseTest):
@@ -47,6 +55,19 @@
         TelephonyBaseTest.setup_class(self)
         self.message_lengths = (50, 160, 180)
         self.tel_logger = TelephonyMetricLogger.for_test_case()
+        if getattr(self.android_devices[0], 'mep', False):
+            start_time = time.monotonic()
+            timeout = start_time + _WAIT_TIME_FOR_MEP_ENABLE
+            while time.monotonic() < timeout:
+                mep_logs = self.android_devices[0].search_logcat(
+                    "UNSOL_SIM_SLOT_STATUS_CHANGED")
+                if mep_logs:
+                    for mep_log in mep_logs:
+                        if "num_ports=2" in mep_log["log_message"]:
+                            break
+                time.sleep(_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL)
+            else:
+                self.log.warning("Couldn't found MEP enabled logs.")
 
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
@@ -405,10 +426,16 @@
     @test_tracker_info(uuid="0e8801f8-7203-45ba-aff3-cb667fd538e1")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_volte_psim_dds_slot_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["volte", "volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="d54c2b4e-2e32-49f0-9536-879eb6f6577e")
     @TelephonyBaseTest.tel_test_wrap
@@ -421,18 +448,30 @@
     @test_tracker_info(uuid="feed9119-df31-46f7-afd8-addf4052422a")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_volte_psim_dds_slot_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["volte", "volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="1da9965c-c863-4e6e-9374-a082fa16d6fd")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mo_volte_esim_dds_slot_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["volte", "volte"], msg="SMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="64aec600-851f-4bde-b66c-130c69d1d5b6")
     @TelephonyBaseTest.tel_test_wrap
@@ -445,10 +484,16 @@
     @test_tracker_info(uuid="9ce40c2c-3a59-4612-a0cc-4fcba887856c")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_sms_mt_volte_esim_dds_slot_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["volte", "volte"], msg="SMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="4e46081d-733d-47d9-be4d-9e492de38bcd")
     @TelephonyBaseTest.tel_test_wrap
@@ -981,10 +1026,16 @@
     @test_tracker_info(uuid="1d72b01d-5ca7-4899-ae57-ecbeff09bc39")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_volte_psim_dds_slot_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            0, None, 1, mo_rat=["volte", "volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="ca2ad510-7f5e-49e4-861e-d433f86c2237")
     @TelephonyBaseTest.tel_test_wrap
@@ -997,18 +1048,30 @@
     @test_tracker_info(uuid="63a0480a-18dd-43e5-82e9-45e008346ea9")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_volte_psim_dds_slot_1(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 0, 1, mt_rat=["volte", "volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_0,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="5e51f0d9-f1b6-4bfe-88ab-f28ebaa6ee55")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mo_volte_esim_dds_slot_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            1, None, 0, mo_rat=["volte", "volte"], msg="MMS", direction="mo")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="fcc7e8aa-41a4-48a1-9586-d6080c77a79b")
     @TelephonyBaseTest.tel_test_wrap
@@ -1021,10 +1084,16 @@
     @test_tracker_info(uuid="f633bf56-2d15-462b-994d-e9294d87ca23")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_mms_mt_volte_esim_dds_slot_0(self):
-        return dsds_message_test(
+        return dsds_message_streaming_test(
             self.log,
             self.android_devices,
-            None, 1, 0, mt_rat=["volte", "volte"], msg="MMS", direction="mt")
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=0,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="3c336061-32cf-4e9a-bb1e-b54e3357e644")
     @TelephonyBaseTest.tel_test_wrap
@@ -1686,4 +1755,229 @@
         if not self._test_msim_voice_call_in_collision_with_mt_sms(
             1, 1, 0, rat=["volte", "volte"], call_direction="mt"):
             result =  False
-        return result
\ No newline at end of file
+        return result
+
+    # e+e
+    @test_tracker_info(uuid="7acde3ba-9478-4feb-924c-ff48b7c1faa6")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mo_volte_esim_port_0_dds_slot_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="6819f18a-afc2-4d90-9db1-701e098002fc")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mt_volte_esim_port_0_dds_slot_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="55090c8a-43e7-452c-94dd-4f49070999d3")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mo_volte_esim_port_0_dds_slot_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=2,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="ac211113-66bd-447b-9164-287fd410411c")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mt_volte_esim_port_0_dds_slot_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=2,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="52d7db08-333e-4a3e-93f2-79413c107efc")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mo_volte_esim_port_1_dds_slot_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=2,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="a4d9fbd5-6288-4a5a-ab2f-1d823c05eb00")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mt_volte_esim_port_1_dds_slot_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=2,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="2aadcb03-1b36-47dd-ad0a-096e934210ff")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mo_volte_esim_port_1_dds_slot_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="50653a52-ea60-4186-9d57-34fe06743c80")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_sms_mt_volte_esim_port_1_dds_slot_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=1,
+            msg_type="SMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="6068c87d-93c9-43eb-a355-c2233230e3bd")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mo_volte_esim_port_0_dds_slot_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="7e5620fe-6f8d-4d00-a1ad-a633d5929980")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mt_volte_esim_port_0_dds_slot_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="15829355-42c0-4e15-b17b-c269e5c28801")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mo_volte_esim_port_0_dds_slot_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=2,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="9f95ad3c-9af0-4475-9f8b-b2264bb65190")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mt_volte_esim_port_0_dds_slot_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_1,
+            dds_slot=2,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="8453ca48-8fff-448a-b085-e94d7844f5e0")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mo_volte_esim_port_1_dds_slot_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=2,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="0fbbf77d-063e-4f7b-87a9-1aab268b36f1")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mt_volte_esim_port_1_dds_slot_2(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=2,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="e02e9bc0-bbd7-440a-b784-70c895036a42")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mo_volte_esim_port_1_dds_slot_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="59b06e08-ac7f-429c-a67c-83b37b049602")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_mms_mt_volte_esim_port_1_dds_slot_1(self):
+        return dsds_message_streaming_test(
+            self.log,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            test_slot=SimSlotInfo.SLOT_2,
+            dds_slot=1,
+            msg_type="MMS",
+            direction="mt",
+            streaming=False)
\ No newline at end of file
diff --git a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSVoiceTest.py b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSVoiceTest.py
index c51e078..337b019 100644
--- a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSVoiceTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSVoiceTest.py
@@ -14,17 +14,38 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import time
+
+from acts import signals
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger
+from acts_contrib.test_utils.tel.tel_defines import SimSlotInfo
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_call_streaming_test
 from acts_contrib.test_utils.tel.tel_dsds_utils import dsds_voice_call_test
 from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
 
+_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL = 60
+_WAIT_TIME_FOR_MEP_ENABLE = 180
+
 
 class TelLiveGFTDSDSVoiceTest(TelephonyBaseTest):
     def setup_class(self):
         TelephonyBaseTest.setup_class(self)
         self.tel_logger = TelephonyMetricLogger.for_test_case()
+        if getattr(self.android_devices[0], 'mep', False):
+            start_time = time.monotonic()
+            timeout = start_time + _WAIT_TIME_FOR_MEP_ENABLE
+            while time.monotonic() < timeout:
+                mep_logs = self.android_devices[0].search_logcat(
+                    "UNSOL_SIM_SLOT_STATUS_CHANGED")
+                if mep_logs:
+                    for mep_log in mep_logs:
+                        if "num_ports=2" in mep_log["log_message"]:
+                            break
+                time.sleep(_WAIT_TIME_FOR_MEP_ENABLE_INTERVAL)
+            else:
+                self.log.warning("Couldn't found MEP enabled logs.")
 
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
@@ -39,9 +60,16 @@
     @test_tracker_info(uuid="7631b805-48b6-4b91-99a3-eef392e5b0fc")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_voice_call_mo_volte_psim_dds_slot_1(self):
-        return dsds_voice_call_test(
-            self.log, self.tel_logger, self.android_devices,
-            0, None, 1, mo_rat=["volte", "volte"], call_direction="mo")
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="4771e517-08cf-4169-afe7-fe3e41f05c45")
     @TelephonyBaseTest.tel_test_wrap
@@ -53,16 +81,30 @@
     @test_tracker_info(uuid="e8f914df-cada-4187-ab53-734624c9c941")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_voice_call_mt_volte_psim_dds_slot_1(self):
-        return dsds_voice_call_test(
-            self.log, self.tel_logger, self.android_devices,
-            None, 0, 1, mt_rat=["volte", "volte"], call_direction="mt")
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_0,
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="967a665a-9614-4fe4-b293-e20b66637802")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_voice_call_mo_volte_esim_dds_slot_0(self):
-        return dsds_voice_call_test(
-            self.log, self.tel_logger, self.android_devices,
-            1, None, 0, mo_rat=["volte", "volte"], call_direction="mo")
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
 
     @test_tracker_info(uuid="901c7fa3-039f-4888-90eb-82af587fa8dd")
     @TelephonyBaseTest.tel_test_wrap
@@ -74,9 +116,169 @@
     @test_tracker_info(uuid="a78f2808-a6c6-4483-b7f5-ad1ec925dd52")
     @TelephonyBaseTest.tel_test_wrap
     def test_msim_voice_call_mt_volte_esim_dds_slot_0(self):
-        return dsds_voice_call_test(
-            self.log, self.tel_logger, self.android_devices,
-            None, 1, 0, mt_rat=["volte", "volte"], call_direction="mt")
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_0, SimSlotInfo.SLOT_1],
+            test_rat=["volte", "volte"],
+            dds_slot=0,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
+
+    # e+e
+    @test_tracker_info(uuid="618fc3c8-1cb9-4346-957e-bd129e3a5426")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_call_mo_volte_esim_port_0_dds_slot_1(self):
+        """A MO VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 VoLTE
+            - eSIM port 1 VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="5374d0a5-5846-43cf-af4d-db4c76abaf66")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_call_mt_volte_esim_port_0_dds_slot_1(self):
+        """A MT VoLTE call received at eSIM port 0, where
+            - eSIM port 0 VoLTE
+            - eSIM port 1 VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="b10751c8-57c0-42b9-870b-9b99fd9fc9f8")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_call_mo_volte_esim_port_0_dds_slot_2(self):
+        """A MO VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 VoLTE
+            - eSIM port 1 VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="5b7fab2e-7bdc-4668-98f9-3c9c4eb07a43")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_call_mt_volte_esim_port_0_dds_slot_2(self):
+        """A MT VoLTE call dialed at eSIM port 0, where
+            - eSIM port 0 VoLTE
+            - eSIM port 1 VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_1,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="b0c42973-319e-4fa0-8f32-7fc07862a229")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_call_mo_volte_esim_port_1_dds_slot_2(self):
+        """A MO VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 VoLTE
+            - eSIM port 1 VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="5ddd3e86-e568-4bd8-acd7-f9f44a802b2d")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_call_mt_volte_esim_port_1_dds_slot_2(self):
+        """A MT VoLTE call received at eSIM port 1, where
+            - eSIM port 0 VoLTE
+            - eSIM port 1 VoLTE
+            - DDS at eSIM port 1 (SimSlotInfo.SLOT_2)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            dds_slot=2,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mt",
+            streaming=False)
+
+    @test_tracker_info(uuid="7bf3a669-3776-4906-8b68-0fa456e8870f")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_call_mo_volte_esim_port_1_dds_slot_1(self):
+        """A MO VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 VoLTE
+            - eSIM port 1 VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mo",
+            streaming=False)
+
+    @test_tracker_info(uuid="31c7cfe1-13ff-42a4-90f3-cdfdf678ba19")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_msim_voice_call_mt_volte_esim_port_1_dds_slot_1(self):
+        """A MT VoLTE call dialed at eSIM port 1, where
+            - eSIM port 0 VoLTE
+            - eSIM port 1 VoLTE
+            - DDS at eSIM port 0 (SimSlotInfo.SLOT_1)
+        """
+        return dsds_call_streaming_test(
+            self.log,
+            self.tel_logger,
+            self.android_devices,
+            sim_slot=[SimSlotInfo.SLOT_1, SimSlotInfo.SLOT_2],
+            test_rat=["volte", "volte"],
+            dds_slot=1,
+            test_slot=SimSlotInfo.SLOT_2,
+            direction="mt",
+            streaming=False)
 
     @test_tracker_info(uuid="f6994dbd-c5a0-42c7-a43d-67227f5dfb88")
     @TelephonyBaseTest.tel_test_wrap
diff --git a/acts_tests/tests/google/tel/live/TelLiveSettingsTest.py b/acts_tests/tests/google/tel/live/TelLiveSettingsTest.py
index c49a622..72f8919 100644
--- a/acts_tests/tests/google/tel/live/TelLiveSettingsTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveSettingsTest.py
@@ -24,24 +24,18 @@
 from acts.keys import Config
 from acts.utils import unzip_maintain_permissions
 from acts.test_decorators import test_tracker_info
+from acts_contrib.test_utils.net import ui_utils
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
 from acts_contrib.test_utils.tel.tel_defines import GEN_4G
 from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
-from acts_contrib.test_utils.tel.tel_defines import MOBILE_DATA
-from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
-from acts_contrib.test_utils.tel.tel_defines import USE_SIM
-from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
 from acts_contrib.test_utils.tel.tel_bootloader_utils import flash_radio
 from acts_contrib.test_utils.tel.tel_logging_utils import set_qxdm_logger_command
 from acts_contrib.test_utils.tel.tel_subscription_utils import get_slot_index_from_subid
 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_phone_subscription
-from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_volte
 from acts_contrib.test_utils.tel.tel_test_utils import dumpsys_carrier_config
 from acts_contrib.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id
-from acts_contrib.test_utils.tel.tel_test_utils import is_droid_in_network_generation
 from acts_contrib.test_utils.tel.tel_test_utils import is_sim_locked
-from acts_contrib.test_utils.tel.tel_test_utils import get_current_override_network_type
 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 print_radio_info
@@ -49,8 +43,10 @@
 from acts_contrib.test_utils.tel.tel_test_utils import system_file_push
 from acts_contrib.test_utils.tel.tel_test_utils import unlock_sim
 from acts_contrib.test_utils.tel.tel_test_utils import verify_default_telephony_setting
-from acts_contrib.test_utils.tel.tel_ops_utils import get_resource_value
-from acts_contrib.test_utils.tel.tel_ops_utils import wait_and_click_element
+from acts_contrib.test_utils.tel.tel_settings_utils import att_apn_test
+from acts_contrib.test_utils.tel.tel_settings_utils import tmo_apn_test
+from acts_contrib.test_utils.tel.tel_settings_utils import toggle_mobile_data_test
+from acts_contrib.test_utils.tel.tel_settings_utils import toggle_sim_test
 from acts.utils import set_mobile_data_always_on
 from acts.libs.utils.multithread import multithread_func
 
@@ -63,7 +59,8 @@
         self.stress_test_number = self.get_stress_test_number()
         self.carrier_configs = dumpsys_carrier_config(self.dut)
         self.dut_subID = get_outgoing_voice_sub_id(self.dut)
-        self.dut_capabilities = self.dut.telephony["subscription"][self.dut_subID].get("capabilities", [])
+        self.dut_capabilities = self.dut.telephony["subscription"][
+            self.dut_subID].get("capabilities", [])
 
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
@@ -97,7 +94,6 @@
         2. Check the carrier_configs are expected value.
 
         """
-        pass
 
     @test_tracker_info(uuid="64deba57-c1c2-422f-b771-639c95edfbc0")
     @TelephonyBaseTest.tel_test_wrap
@@ -362,57 +358,7 @@
             True is tests passes else False
         """
         ad = self.android_devices[0]
-
-        if not phone_setup_volte(ad.log, ad):
-            ad.log.error('Phone failed to enable LTE')
-            return False
-        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
-
-        ad.adb.shell('am start -a android.settings.WIRELESS_SETTINGS')
-        wait_and_click_element(ad, 'SIMs')
-
-        switch_value = get_resource_value(ad, USE_SIM)
-        if switch_value == 'true':
-            ad.log.info('SIM is enabled as expected')
-        else:
-            ad.log.error('SIM should be enabled but SIM is disabled')
-            return False
-
-        label_text = USE_SIM
-        label_resource_id = 'com.android.settings:id/switch_text'
-
-        ad.log.info('Disable SIM')
-        wait_and_click_element(ad, label_text, label_resource_id)
-
-        button_resource_id = 'android:id/button1'
-        wait_and_click_element(ad, 'Yes', button_resource_id)
-        switch_value = get_resource_value(ad, USE_SIM)
-        if switch_value == 'false':
-            ad.log.info('SIM is disabled as expected')
-        else:
-            ad.log.error('SIM should be disabled but SIM is enabled')
-            return False
-
-        ad.log.info('Enable SIM')
-        wait_and_click_element(ad, label_text, label_resource_id)
-
-        wait_and_click_element(ad, 'Yes', button_resource_id)
-        switch_value = get_resource_value(ad, USE_SIM)
-        if switch_value == 'true':
-            ad.log.info('SIM is enabled as expected')
-        else:
-            ad.log.error('SIM should be enabled but SIM is disabled')
-            return False
-
-        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
-
-        if is_droid_in_network_generation(self.log, ad, GEN_4G,
-                                            NETWORK_SERVICE_DATA):
-            ad.log.info('Success! attached on LTE')
-        else:
-            ad.log.error('Failure - expected LTE, current %s',
-                         get_current_override_network_type(ad))
-            return False
+        return toggle_sim_test(ad, GEN_4G)
 
     @test_tracker_info(uuid='dc0d381b-2dbf-4e25-87a4-53ec657e12d1')
     @TelephonyBaseTest.tel_test_wrap
@@ -434,42 +380,96 @@
         """
         ad = self.android_devices[0]
 
-        if not phone_setup_volte(ad.log, ad):
-            ad.log.error('Phone failed to enable LTE')
-            return False
-        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
+        return toggle_mobile_data_test(ad, GEN_4G)
 
-        ad.adb.shell('am start -a android.settings.WIRELESS_SETTINGS')
-        wait_and_click_element(ad, 'SIMs')
-        switch_value = get_resource_value(ad, MOBILE_DATA)
+    @test_tracker_info(uuid='9f0cb1cd-6dff-4736-a7f7-3d08af782575')
+    @TelephonyBaseTest.tel_test_wrap
+    def test_att_apn_settings_sms_lte(self):
+        """Test ATT APN and SMS
 
-        if switch_value == 'true':
-            ad.log.info('Mobile data is enabled as expected')
-        else:
-            ad.log.error('Mobile data should be enabled but it is disabled')
+        Steps:
+            1. Provision device to LTE
+            2. Launch Settings - Network & Internet
+            3. Click on SIMs
+            4. Click on Access Point Names
+            5. Add New APN
+            6. Save New APN
+            7. Switch APN to New APN
+            8. Check Network is connected to LTE
+            9. Send SMS
 
-        ad.log.info('Disable mobile data')
-        ad.droid.telephonyToggleDataConnection(False)
-        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
-        switch_value = get_resource_value(ad, MOBILE_DATA)
-        if switch_value == 'false':
-            ad.log.info('Mobile data is disabled as expected')
-        else:
-            ad.log.error('Mobile data should be disabled but it is enabled')
+        Returns:
+            True is tests passes else False
+        """
+        caller, callee = self.android_devices[0], self.android_devices[1]
 
-        ad.log.info('Enabling mobile data')
-        ad.droid.telephonyToggleDataConnection(True)
-        time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
-        switch_value = get_resource_value(ad, MOBILE_DATA)
-        if switch_value == 'true':
-            ad.log.info('Mobile data is enabled as expected')
-        else:
-            ad.log.error('Mobile data should be enabled but it is disabled')
+        return att_apn_test(self.log, caller, callee, GEN_4G, msg_type='sms')
 
-        if is_droid_in_network_generation(self.log, ad, GEN_4G,
-                                            NETWORK_SERVICE_DATA):
-            ad.log.info('Success! attached on LTE')
-        else:
-            ad.log.error('Failure - expected LTE, current %s',
-                         get_current_override_network_type(ad))
-            return False
\ No newline at end of file
+    @test_tracker_info(uuid='7ba6eccd-5115-495a-8298-a1b41e5115d8')
+    @TelephonyBaseTest.tel_test_wrap
+    def test_att_apn_settings_mms_lte(self):
+        """Test ATT APN and MMS
+
+        Steps:
+            1. Provision device to LTE
+            2. Launch Settings - Network & Internet
+            3. Click on SIMs
+            4. Click on Access Point Names
+            5. Add New APN
+            6. Add ATT APN details and Save
+            7. Switch APN to New APN
+            8. Check Network is connected to LTE
+            9. Send MMS
+
+        Returns:
+            True is tests passes else False
+        """
+        caller, callee = self.android_devices[0], self.android_devices[1]
+
+        return att_apn_test(self.log, caller, callee, GEN_4G, msg_type='mms')
+
+    @test_tracker_info(uuid='ea6886e0-5a34-4b62-9a2b-d81d109284ae')
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tmo_apn_settings_sms_lte(self):
+        """Test TMO APN and SMS
+
+        Steps:
+            1. Provision device to LTE
+            2. Launch Settings - Network & Internet
+            3. Click on SIMs
+            4. Click on Access Point Names
+            5. Add New APN
+            6. Add TMO APN details and Save New APN
+            7. Switch APN to New APN
+            8. Check Network is connected to LTE
+            9. Send SMS
+
+        Returns:
+            True is tests passes else False
+        """
+        caller, callee = self.android_devices[0], self.android_devices[1]
+
+        return tmo_apn_test(self.log, caller, callee, GEN_4G, msg_type='sms')
+
+    @test_tracker_info(uuid='08b4b549-cacb-481a-b6ea-7368b5608009')
+    @TelephonyBaseTest.tel_test_wrap
+    def test_tmo_apn_settings_mms_lte(self):
+        """Test APN test and MMS
+
+        Steps:
+            1. Provision device to LTE
+            2. Launch Settings - Network & Internet
+            3. Click on SIMs
+            4. Click on Access Point Names
+            5. Add New APN
+            6. Add TMO APN details and Save
+            7. Switch APN to New APN
+            8. Check Network is connected to LTE
+            9. Send MMS
+
+        Returns:
+            True is tests passes else False
+        """
+        caller, callee = self.android_devices[0], self.android_devices[1]
+
+        return tmo_apn_test(self.log, caller, callee, GEN_4G, msg_type='mms')
diff --git a/acts_tests/tests/google/tel/live/TelLiveSmsTest.py b/acts_tests/tests/google/tel/live/TelLiveSmsTest.py
index d03d42a..0b1cc56 100644
--- a/acts_tests/tests/google/tel/live/TelLiveSmsTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveSmsTest.py
@@ -34,6 +34,8 @@
 from acts_contrib.test_utils.tel.tel_message_utils import message_test
 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_phones_idle
+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_test_utils import get_operator_name
 from acts_contrib.test_utils.tel.tel_test_utils import install_message_apk
 from acts.utils import rand_ascii_str
@@ -56,9 +58,9 @@
     def teardown_test(self):
         ensure_phones_idle(self.log, self.android_devices)
 
-    def _get_wfc_mode(self, ad):
+    def _get_wfc_mode(self, ad, sub_id):
         # Verizon doesn't supports wfc mode as WFC_MODE_WIFI_PREFERRED
-        carrier = ad.adb.getprop("gsm.sim.operator.alpha")
+        carrier = ad.telephony["subscription"][sub_id]["operator"]
         if carrier == CARRIER_VZW:
             wfc = WFC_MODE_CELLULAR_PREFERRED
         else:
@@ -69,7 +71,7 @@
         carrier = ad.adb.getprop("gsm.sim.operator.alpha")
 
         if int(ad.adb.getprop("ro.product.first_api_level")) > 30 and (
-                carrier == CARRIER_VZW):
+                carrier == "Verizon"):
             raise signals.TestSkip(
                 "Device Doesn't Support 2g/3G Band.")
 
@@ -1459,7 +1461,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_outgoing_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[0],
@@ -1485,7 +1489,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_incoming_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[1],
@@ -1511,7 +1517,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_outgoing_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[0],
@@ -1538,7 +1546,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_incoming_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[1],
@@ -1565,7 +1575,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_outgoing_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[0],
@@ -1589,7 +1601,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_incoming_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[1],
@@ -1613,7 +1627,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_outgoing_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[0],
@@ -1638,7 +1654,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_incoming_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[1],
@@ -1758,7 +1776,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_outgoing_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[0],
@@ -1785,7 +1805,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_incoming_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[1],
@@ -1812,7 +1834,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_outgoing_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[0],
@@ -1840,7 +1864,9 @@
         Returns:
             True if pass; False if fail.
         """
-        _wfc_mode = self._get_wfc_mode(self.android_devices[0])
+        _wfc_mode = self._get_wfc_mode(
+            self.android_devices[0],
+            get_incoming_message_sub_id(self.android_devices[0]))
         return message_test(
             self.log,
             self.android_devices[1],
diff --git a/acts_tests/tests/google/tel/live/TelLiveStressCallTest.py b/acts_tests/tests/google/tel/live/TelLiveStressCallTest.py
index 573ca60..a7520f3 100644
--- a/acts_tests/tests/google/tel/live/TelLiveStressCallTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveStressCallTest.py
@@ -21,6 +21,7 @@
 import time
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+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_PREFERRED
 from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
 from acts_contrib.test_utils.tel.tel_ims_utils import set_wfc_mode
@@ -72,7 +73,7 @@
     def on_fail(self, test_name, begin_time):
         pass
 
-    def _setup_wfc(self):
+    def _setup_wfc(self, wfc_mode):
         for ad in self.android_devices:
             if not ensure_wifi_connected(
                     ad.log,
@@ -83,7 +84,7 @@
                 ad.log.error("Phone Wifi connection fails.")
                 return False
             ad.log.info("Phone WIFI is connected successfully.")
-            if not set_wfc_mode(self.log, ad, WFC_MODE_WIFI_PREFERRED):
+            if not set_wfc_mode(self.log, ad, wfc_mode):
                 ad.log.error("Phone failed to enable Wifi-Calling.")
                 return False
             ad.log.info("Phone is set in Wifi-Calling successfully.")
@@ -93,19 +94,19 @@
             ad.log.info("Phone is in WFC enabled state.")
         return True
 
-    def _setup_wfc_apm(self):
+    def _setup_wfc_apm(self, wfc_mode):
         for ad in self.android_devices:
-            toggle_airplane_mode(ad.log, ad, True)
             if not ensure_wifi_connected(
                     ad.log,
                     ad,
                     self.wifi_network_ssid,
                     self.wifi_network_pass,
-                    retries=3):
+                    retries=3,
+                    apm=True):
                 ad.log.error("Phone Wifi connection fails.")
                 return False
             ad.log.info("Phone WIFI is connected successfully.")
-            if not set_wfc_mode(self.log, ad, WFC_MODE_WIFI_PREFERRED):
+            if not set_wfc_mode(self.log, ad, wfc_mode):
                 ad.log.error("Phone failed to enable Wifi-Calling.")
                 return False
             ad.log.info("Phone is set in Wifi-Calling successfully.")
@@ -115,7 +116,8 @@
             ad.log.info("Phone is in WFC enabled state.")
         return True
 
-    def _setup_vt(self):
+    def _setup_vt(self, *args):
+        del args
         ads = self.android_devices
         tasks = [(phone_setup_video, (self.log, ads[0])), (phone_setup_video,
                                                            (self.log, ads[1]))]
@@ -124,7 +126,8 @@
             return False
         return True
 
-    def _setup_lte_volte_enabled(self):
+    def _setup_lte_volte_enabled(self, *args):
+        del args
         for ad in self.android_devices:
             if not phone_setup_volte(self.log, ad):
                 ad.log.error("Phone failed to enable VoLTE.")
@@ -132,7 +135,8 @@
             ad.log.info("Phone VOLTE is enabled successfully.")
         return True
 
-    def _setup_lte_volte_disabled(self):
+    def _setup_lte_volte_disabled(self, *args):
+        del args
         for ad in self.android_devices:
             if not phone_setup_csfb(self.log, ad):
                 ad.log.error("Phone failed to setup CSFB.")
@@ -140,7 +144,8 @@
             ad.log.info("Phone VOLTE is disabled successfully.")
         return True
 
-    def _setup_3g(self):
+    def _setup_3g(self, *args):
+        del args
         for ad in self.android_devices:
             if not phone_setup_voice_3g(self.log, ad):
                 ad.log.error("Phone failed to setup 3g.")
@@ -148,7 +153,8 @@
             ad.log.info("Phone RAT 3G is enabled successfully.")
         return True
 
-    def _setup_2g(self):
+    def _setup_2g(self, *args):
+        del args
         for ad in self.android_devices:
             if not phone_setup_voice_2g(self.log, ad):
                 ad.log.error("Phone failed to setup 2g.")
@@ -179,6 +185,7 @@
 
     def stress_test(self,
                     setup_func=None,
+                    setup_arg=WFC_MODE_WIFI_PREFERRED,
                     network_check_func=None,
                     test_sms=False,
                     test_video=False):
@@ -186,8 +193,12 @@
             #check for sim and service
             ensure_phone_subscription(self.log, ad)
 
-        if setup_func and not setup_func():
+        setup_begin_time = get_current_epoch_time()
+        if setup_func and not setup_func(setup_arg):
             self.log.error("Test setup %s failed", setup_func.__name__)
+            self._take_bug_report(
+                "%s_%s" % (self.test_name, setup_func.__name__),
+                setup_begin_time)
             return False
         fail_count = collections.defaultdict(int)
         for i in range(1, self.phone_call_iteration + 1):
@@ -363,11 +374,12 @@
 
     @test_tracker_info(uuid="be45c620-b45b-4a06-8424-b17d744d0735")
     @TelephonyBaseTest.tel_test_wrap
-    def test_call_wifi_calling_stress_apm(self):
+    def test_call_wifi_calling_wifi_preferred_stress_apm(self):
         """ Wifi calling in AirPlaneMode call stress test
 
         Steps:
-        1. Make Sure PhoneA and PhoneB in WFC On + APM ON + Wifi Connected
+        1. Make Sure PhoneA and PhoneB in WFC On(wifi preferred) +
+            APM ON + Wifi Connected
         2. Call from PhoneA to PhoneB, hang up on PhoneA.
         3, Repeat 2 around N times based on the config setup
 
@@ -383,6 +395,30 @@
             setup_func=self._setup_wfc_apm,
             network_check_func=is_phone_in_call_iwlan)
 
+    @test_tracker_info(uuid="d0e52109-b359-4efa-bbaa-ca758428b654")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_wifi_calling_cellular_preferred_stress_apm(self):
+        """ Wifi calling in AirPlaneMode call stress test
+
+        Steps:
+        1. Make Sure PhoneA and PhoneB in WFC On(cellular preferred) +
+            APM ON + Wifi Connected
+        2. Call from PhoneA to PhoneB, hang up on PhoneA.
+        3, Repeat 2 around N times based on the config setup
+
+        Expected Results:
+        1, Verify phone is at IDLE state
+        2, Verify the phone is at ACTIVE, if it is in dialing, then we retry
+        3, Verify the phone is IDLE after hung up
+
+        Returns:
+            True if pass; False if fail.
+        """
+        return self.stress_test(
+            setup_func=self._setup_wfc_apm,
+            setup_arg=WFC_MODE_CELLULAR_PREFERRED,
+            network_check_func=is_phone_in_call_iwlan)
+
     @test_tracker_info(uuid="8af0454b-b4db-46d8-b5cc-e13ec5bc59ab")
     @TelephonyBaseTest.tel_test_wrap
     def test_call_3g_stress(self):
diff --git a/acts_tests/tests/google/tel/live/TelLiveStressFdrTest.py b/acts_tests/tests/google/tel/live/TelLiveStressFdrTest.py
index 91e1332..efcc980 100644
--- a/acts_tests/tests/google/tel/live/TelLiveStressFdrTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveStressFdrTest.py
@@ -58,8 +58,8 @@
         TelephonyBaseTest.setup_class(self)
 
         self.user_params["telephony_auto_rerun"] = 0
-        self.stress_test_number = int(
-            self.user_params.get("stress_test_number", 100))
+        self.fdr_stress_cycle = int(
+            self.user_params.get("fdr_stress_cycle", 100))
         self.skip_reset_between_cases = False
 
         self.dut = self.android_devices[0]
@@ -338,14 +338,14 @@
         fail_count = collections.defaultdict(int)
         test_result = True
 
-        for i in range(1, self.stress_test_number + 1):
+        for i in range(1, self.fdr_stress_cycle + 1):
             begin_time = get_current_epoch_time()
             test_name = "%s_iteration_%s" % (self.test_name, i)
             log_msg = "[Test Case] %s" % test_name
             self.log.info("%s begin", log_msg)
             self.dut.droid.logI("%s begin" % log_msg)
             test_msg = "FDR Stress Test %s Iteration <%s> / <%s>" % (
-                self.test_name, i, self.stress_test_number)
+                self.test_name, i, self.fdr_stress_cycle)
             self.log.info(test_msg)
             fastboot_wipe(self.dut)
             self.log.info("%s wait %s secs for radio up.",
@@ -374,7 +374,7 @@
         for failure, count in fail_count.items():
             if count:
                 self.log.error("%s failure count = %s in total %s iterations",
-                               failure, count, self.stress_test_number)
+                               failure, count, self.fdr_stress_cycle)
                 test_result = False
         return test_result
 
diff --git a/acts_tests/tests/google/tel/live/TelLiveStressTest.py b/acts_tests/tests/google/tel/live/TelLiveStressTest.py
index e04ba6a..c46c2ed 100644
--- a/acts_tests/tests/google/tel/live/TelLiveStressTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveStressTest.py
@@ -120,6 +120,7 @@
                  8: "CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED",
                  9: "CALL_HANGUP_FAIL",
                  10: "CALL_ID_CLEANUP_FAIL"}
+voice_call_failure_dict = {}
 
 
 class TelLiveStressTest(TelephonyBaseTest):
@@ -169,6 +170,7 @@
         self.cbrs_check_interval = int(
             self.user_params.get("cbrs_check_interval", 100))
         self.dut_incall = False
+        self.wfc_nw_gen = self.user_params.get("wfc_nw_gen", None)
         self.dsds_esim = self.user_params.get("dsds_esim", False)
         self.cbrs_esim = self.user_params.get("cbrs_esim", False)
         telephony_info = getattr(self.dut, "telephony", {})
@@ -194,6 +196,11 @@
         self.call_stats_check = self.user_params.get("call_stats_check", False)
         self.nsa_5g_for_stress = self.user_params.get("nsa_5g_for_stress", False)
         self.nr_type = self.user_params.get("nr_type", 'nsa')
+        self.idle_period = self.user_params.get("idle_period", False)
+        self.min_pause_duration = int(self.user_params.get("min_pause_duration", 180))
+        self.max_pause_duration = int(self.user_params.get("max_pause_duration", 600))
+        self.call_pause_intervals = int(self.user_params.get("call_pause_intervals", 10))
+        self.pause=False
         return True
 
     def setup_test(self):
@@ -252,7 +259,8 @@
         for ad in self.android_devices:
             if not phone_setup_iwlan(
                     self.log, ad, True, WFC_MODE_CELLULAR_PREFERRED,
-                    self.wifi_network_ssid, self.wifi_network_pass):
+                    self.wifi_network_ssid, self.wifi_network_pass,
+                    nw_gen=self.wfc_nw_gen, nr_type= self.nr_type):
                 ad.log.error("Failed to setup WFC.")
                 return False
         return True
@@ -265,7 +273,8 @@
             ad.log.info("Phone VOLTE is enabled successfully.")
             # TODO: b/186865335 Move 5G methods to NR directory
             if self.nsa_5g_for_stress:
-                if not provision_device_for_5g(self.log, ad):
+                if not provision_device_for_5g(
+                        self.log, ad, nr_type=self.nr_type):
                     ad.log.error("Phone failed to attach 5G NSA.")
                     return False
                 ad.log.info("Phone 5G NSA VOLTE is enabled successfully.")
@@ -310,6 +319,9 @@
 
     def _send_message(self, max_wait_time=2 * MAX_WAIT_TIME_SMS_RECEIVE):
         slot_id_rx = None
+        if self.pause and self.idle_period:
+            self.log.info("PAUSE MESSAGE TEST FOR %s seconds", self.pause_duration)
+            time.sleep(self.pause_duration)
         if self.single_phone_test:
             ads = [self.dut, self.dut]
         else:
@@ -434,6 +446,16 @@
         log_msg = "[Test Case] %s" % test_name
         self.log.info("%s for %s seconds begin", log_msg, duration)
 
+        if self.idle_period:
+            call_iteration = self.call_pause_intervals if self.call_pause_intervals != 0 else 1
+            if the_number % call_iteration == 0:
+                self.pause=True
+                self.pause_duration = random.randrange(
+                    self.min_pause_duration, self.max_pause_duration)
+                self.log.info("PAUSE CALLING TEST FOR %s seconds", self.pause_duration)
+                time.sleep(self.pause_duration)
+                self.pause=False
+
         if self.call_stats_check:
             voice_type_init = check_voice_network_type(ads, voice_init=True)
         else:
@@ -475,10 +497,7 @@
                 self.log,
                 self.dut,
                 self.call_server_number,
-                incall_ui_display=INCALL_UI_DISPLAY_BACKGROUND,
-                call_stats_check=self.call_stats_check,
-                voice_type_init=voice_type_init,
-                result_info = self.result_info
+                incall_ui_display=INCALL_UI_DISPLAY_BACKGROUND
             ) and wait_for_in_call_active(self.dut, 60, 3)
         else:
             call_setup_result = call_setup_teardown(
@@ -494,7 +513,6 @@
                 call_stats_check=self.call_stats_check,
                 voice_type_init=voice_type_init,
                 result_info = self.result_info)
-            self.result_collection[RESULTS_LIST[call_setup_result.result_value]] += 1
 
         if not call_setup_result:
             get_telephony_signal_strength(ads[0])
@@ -516,6 +534,12 @@
                     return True
             self.log.error("%s: Setup Call failed.", log_msg)
             failure_reasons.add("Setup")
+            if self.call_stats_check:
+                network = ads[0].droid.telephonyGetCurrentVoiceNetworkType()
+                ads[0].log.debug("Call Setup failure RAT is %s", network)
+                self.result_info["Call Failures"] = self._update_call_failure(str(ads[0].serial),
+                                                                             "Call Setup Failure",
+                                                                             network)
             result = False
         else:
             elapsed_time = 0
@@ -548,6 +572,13 @@
                                             1)
                                     continue
                         failure_reasons.add("Maintenance")
+                        if self.call_stats_check:
+                            network = ad.droid.telephonyGetCurrentVoiceNetworkType()
+                            ad.log.debug("Call Maintenance failure RAT is %s", network)
+                            self.result_info["Call Failures"] = self._update_call_failure(
+                                                                      str(ad.serial),
+                                                                      "Call Maintenance Failure",
+                                                                      network)
                         last_call_drop_reason(ad, begin_time)
                         hangup_call(self.log, ads[0])
                         result = False
@@ -563,7 +594,7 @@
         else:
             if self.nsa_5g_for_stress:
                 for ad in (ads[0], ads[1]):
-                    if not is_current_network_5g(ad, self.nr_type):
+                    if not is_current_network_5g(ad, nr_type=self.nr_type):
                         ad.log.error("Phone not attached on 5G")
         for ad in ads:
             if not wait_for_call_id_clearing(ad,
@@ -602,11 +633,12 @@
                                      self.user_params["gps_log_file"])
             for reason in failure_reasons:
                 self.result_info["Call %s Failure" % reason] += 1
-            for ad in ads:
-                log_path = os.path.join(self.log_path, test_name,
+            if self.get_binder_logs:
+                for ad in ads:
+                    log_path = os.path.join(self.log_path, test_name,
                                         "%s_binder_logs" % ad.serial)
-                os.makedirs(log_path, exist_ok=True)
-                ad.pull_files(BINDER_LOGS, log_path)
+                    os.makedirs(log_path, exist_ok=True)
+                    ad.pull_files(BINDER_LOGS, log_path)
             try:
                 self._take_bug_report(test_name, begin_time)
             except Exception as e:
@@ -768,6 +800,16 @@
         else:
             return True
 
+    def _update_call_failure(self, dut, key, network):
+        if dut not in voice_call_failure_dict.keys():
+            voice_call_failure_dict[dut] = {key:{network:0}}
+        if key not in voice_call_failure_dict[dut].keys():
+            voice_call_failure_dict[dut].update({key:{network:0}})
+        if network not in voice_call_failure_dict[dut][key].keys():
+            voice_call_failure_dict[dut][key].update({network:0})
+        voice_call_failure_dict[dut][key][network] += 1
+        return voice_call_failure_dict
+
     def _cbrs_data_check_test(self, begin_time, expected_cbrs=True,
                               test_time="before"):
         cbrs_fail_count = 0
@@ -806,7 +848,6 @@
                 self.log.error("Too many exception errors, quit test")
                 return False
             self.log.info("%s", dict(self.result_info))
-        self.tel_logger.set_result(self.result_collection)
         if any([
                 self.result_info["Call Setup Failure"],
                 self.result_info["Call Maintenance Failure"],
@@ -839,6 +880,9 @@
     def _data_download(self, file_names=[]):
         begin_time = get_current_epoch_time()
         slot_id = random.randint(0,1)
+        if self.pause and self.idle_period:
+            self.log.info("PAUSE DATA TEST FOR %s seconds", self.pause_duration)
+            time.sleep(self.pause_duration)
         if self.dsds_esim:
             sub_id = get_subid_from_slot_index(self.log, self.dut, slot_id)
             self.dut.log.info("Data - slot_Id %d", slot_id)
@@ -1315,7 +1359,14 @@
     @test_tracker_info(uuid="4212d0e0-fb87-47e5-ba48-9df9a4a6bb9b")
     @TelephonyBaseTest.tel_test_wrap
     def test_voice_performance_stress(self):
-        """ Vocie Performance stress test"""
+        """ Voice Performance stress test"""
         return self.performance_tests()
 
+    @test_tracker_info(uuid="a126793e-6e78-4920-a8c4-b382a444c4b7")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_voice_performance_stress_nr(self):
+        """ Voice Performance stress test"""
+        return self.performance_tests(
+            setup_func=self._setup_lte_volte_enabled)
+
     """ Tests End """
diff --git a/acts_tests/tests/google/tel/live/TelLiveVoiceConfTest.py b/acts_tests/tests/google/tel/live/TelLiveVoiceConfTest.py
index 8e53ea7..473d2e6 100644
--- a/acts_tests/tests/google/tel/live/TelLiveVoiceConfTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveVoiceConfTest.py
@@ -11277,7 +11277,7 @@
             return False
 
         if not self._three_phone_call_mo_add_mt_reject(
-            [ads[0], ads[1], ads[2]], [is_phone_in_call_volte, None], False):
+            [ads[0], ads[1], ads[2]], [is_phone_in_call_iwlan, None], False):
             return False
         return True
 
diff --git a/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py b/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py
index 6ff748b..9fbe733 100644
--- a/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py
+++ b/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py
@@ -60,6 +60,7 @@
 from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_volte
 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_test_utils import get_phone_number
 from acts_contrib.test_utils.tel.tel_test_utils import install_dialer_apk
 from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
 from acts_contrib.test_utils.tel.tel_test_utils import STORY_LINE
@@ -81,9 +82,11 @@
 from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_leave_voice_mail
 from acts_contrib.test_utils.tel.tel_voice_utils import two_phone_call_long_seq
 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 wait_and_answer_call
 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
 from acts_contrib.test_utils.tel.tel_wifi_utils import set_wifi_to_default
+from acts_contrib.test_utils.tel.tel_wifi_utils import wifi_toggle_state
 from acts.libs.utils.multithread import multithread_func
 
 DEFAULT_PING_DURATION = 120  # in seconds
@@ -123,6 +126,12 @@
             raise signals.TestSkip(
                 "Device Doesn't Support 2g/3G Band.")
 
+    def _is_phone_in_call_not_iwlan(self):
+        return is_phone_in_call_not_iwlan(self.log, self.android_devices[0])
+
+    def _is_phone_in_call_iwlan(self):
+        return is_phone_in_call_iwlan(self.log, self.android_devices[0])
+
 
     """ Tests Begin """
 
@@ -3789,17 +3798,13 @@
             True if success.
             False if failed.
         """
-        if not phone_setup_iwlan(self.log, self.android_devices[0], False,
-                                 WFC_MODE_WIFI_PREFERRED,
-                                 self.wifi_network_ssid,
-                                 self.wifi_network_pass):
-            self.android_devices[0].log.error(
-                "Failed to setup IWLAN with NON-APM WIFI WFC on")
-            return False
         return test_call_setup_in_active_youtube_video(
             self.log,
             self.android_devices,
-            rat=None,
+            rat="wfc",
+            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+            wifi_ssid=self.wifi_network_ssid,
+            wifi_pwd=self.wifi_network_pass,
             call_direction=DIRECTION_MOBILE_ORIGINATED)
 
     @test_tracker_info(uuid="275a93d6-1f39-40c8-893f-ff77afd09e54")
@@ -3815,17 +3820,13 @@
             True if success.
             False if failed.
         """
-        if not phone_setup_iwlan(self.log, self.android_devices[0], False,
-                                 WFC_MODE_WIFI_PREFERRED,
-                                 self.wifi_network_ssid,
-                                 self.wifi_network_pass):
-            self.android_devices[0].log.error(
-                "Failed to setup iwlan with APM off and WIFI and WFC on")
-            return False
         return test_call_setup_in_active_youtube_video(
             self.log,
             self.android_devices,
-            rat=None,
+            rat="wfc",
+            wfc_mode=WFC_MODE_WIFI_PREFERRED,
+            wifi_ssid=self.wifi_network_ssid,
+            wifi_pwd=self.wifi_network_pass,
             call_direction=DIRECTION_MOBILE_TERMINATED)
 
     @test_tracker_info(uuid="ea087709-d4df-4223-b80c-1b33bacbd5a2")
@@ -3846,17 +3847,15 @@
             wfc = WFC_MODE_CELLULAR_PREFERRED
         else:
             wfc = WFC_MODE_WIFI_PREFERRED
-        if not phone_setup_iwlan(self.log, self.android_devices[0], True,
-                                 wfc,
-                                 self.wifi_network_ssid,
-                                 self.wifi_network_pass):
-            self.android_devices[0].log.error(
-                "Failed to setup iwlan with APM, WIFI and WFC on")
-            return False
+
         return test_call_setup_in_active_youtube_video(
             self.log,
             self.android_devices,
-            rat=None,
+            rat="wfc",
+            is_airplane_mode=True,
+            wfc_mode=wfc,
+            wifi_ssid=self.wifi_network_ssid,
+            wifi_pwd=self.wifi_network_pass,
             call_direction=DIRECTION_MOBILE_ORIGINATED)
 
     @test_tracker_info(uuid="44cc14e0-60c7-4fdb-ad26-31fdc4e52aaf")
@@ -3877,17 +3876,15 @@
             wfc = WFC_MODE_CELLULAR_PREFERRED
         else:
             wfc = WFC_MODE_WIFI_PREFERRED
-        if not phone_setup_iwlan(self.log, self.android_devices[0], True,
-                                 wfc,
-                                 self.wifi_network_ssid,
-                                 self.wifi_network_pass):
-            self.android_devices[0].log.error(
-                "Failed to setup iwlan with APM, WIFI and WFC on")
-            return False
+
         return test_call_setup_in_active_youtube_video(
             self.log,
             self.android_devices,
-            rat=None,
+            rat="wfc",
+            is_airplane_mode=True,
+            wfc_mode=wfc,
+            wifi_ssid=self.wifi_network_ssid,
+            wifi_pwd=self.wifi_network_pass,
             call_direction=DIRECTION_MOBILE_TERMINATED)
 
     @test_tracker_info(uuid="e115e8a6-25bf-41fc-aeb8-8f4c922c50e4")
@@ -3903,17 +3900,14 @@
             True if success.
             False if failed.
         """
-        if not phone_setup_iwlan(self.log, self.android_devices[0], True,
-                                 WFC_MODE_CELLULAR_PREFERRED,
-                                 self.wifi_network_ssid,
-                                 self.wifi_network_pass):
-            self.android_devices[0].log.error(
-                "Failed to setup iwlan with APM, WIFI and WFC on")
-            return False
         return test_call_setup_in_active_youtube_video(
             self.log,
             self.android_devices,
-            rat=None,
+            rat="wfc",
+            is_airplane_mode=True,
+            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+            wifi_ssid=self.wifi_network_ssid,
+            wifi_pwd=self.wifi_network_pass,
             call_direction=DIRECTION_MOBILE_ORIGINATED)
 
     @test_tracker_info(uuid="d754d3dd-0b02-4f13-bc65-fdafa254196b")
@@ -3929,17 +3923,14 @@
             True if success.
             False if failed.
         """
-        if not phone_setup_iwlan(self.log, self.android_devices[0], True,
-                                 WFC_MODE_CELLULAR_PREFERRED,
-                                 self.wifi_network_ssid,
-                                 self.wifi_network_pass):
-            self.android_devices[0].log.error(
-                "Failed to setup iwlan with APM, WIFI and WFC on")
-            return False
         return test_call_setup_in_active_youtube_video(
             self.log,
             self.android_devices,
-            rat=None,
+            rat="wfc",
+            is_airplane_mode=True,
+            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+            wifi_ssid=self.wifi_network_ssid,
+            wifi_pwd=self.wifi_network_pass,
             call_direction=DIRECTION_MOBILE_TERMINATED)
 
     @test_tracker_info(uuid="88822edf-4c4a-4bc4-9280-2f27ee9e28d5")
@@ -3955,19 +3946,14 @@
             True if success.
             False if failed.
         """
-        if not phone_setup_iwlan(self.log,
-                                 self.android_devices[0],
-                                 True,
-                                 WFC_MODE_CELLULAR_PREFERRED,
-                                 self.wifi_network_ssid,
-                                 self.wifi_network_pass):
-            self.android_devices[0].log.error(
-                "Failed to setup iwlan with APM, WIFI and WFC on")
-            return False
         return test_call_setup_in_active_youtube_video(
             self.log,
             self.android_devices,
-            rat=None,
+            rat="wfc",
+            is_airplane_mode=True,
+            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+            wifi_ssid=self.wifi_network_ssid,
+            wifi_pwd=self.wifi_network_pass,
             call_direction=DIRECTION_MOBILE_ORIGINATED)
 
     @test_tracker_info(uuid="c4b066b0-3cfd-4831-9c61-5d6b132648c4")
@@ -3983,19 +3969,14 @@
             True if success.
             False if failed.
         """
-        if not phone_setup_iwlan(self.log,
-                                 self.android_devices[0],
-                                 True,
-                                 WFC_MODE_CELLULAR_PREFERRED,
-                                 self.wifi_network_ssid,
-                                 self.wifi_network_pass):
-            self.android_devices[0].log.error(
-                "Failed to setup iwlan with APM, WIFI and WFC on")
-            return False
         return test_call_setup_in_active_youtube_video(
             self.log,
             self.android_devices,
-            rat=None,
+            rat="wfc",
+            is_airplane_mode=True,
+            wfc_mode=WFC_MODE_CELLULAR_PREFERRED,
+            wifi_ssid=self.wifi_network_ssid,
+            wifi_pwd=self.wifi_network_pass,
             call_direction=DIRECTION_MOBILE_TERMINATED)
 
     @test_tracker_info(uuid="f367de12-1fd8-488d-816f-091deaacb791")
@@ -4006,7 +3987,7 @@
 
         1. Set the data limit to the current usage
         2. Setup PhoneA WFC mode: WIFI_PREFERRED.
-        3. Make Sure PhoneB is in 3G mode.
+        3. Make Sure PhoneB is in general mode.
         4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
         5. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
 
@@ -4025,7 +4006,7 @@
             tasks = [(phone_setup_iwlan,
                       (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
                        self.wifi_network_ssid, self.wifi_network_pass)),
-                     (phone_setup_voice_3g, (self.log, ads[1]))]
+                     (phone_setup_voice_general, (self.log, ads[1]))]
             if not multithread_func(self.log, tasks):
                 self.log.error("Phone Failed to Set Up Properly.")
                 self.tel_logger.set_result(CallResult("CALL_SETUP_FAILURE"))
@@ -4176,5 +4157,147 @@
             verify_caller_func=is_phone_in_call_csfb,
             verify_callee_func=is_phone_in_call_csfb)
 
+    @test_tracker_info(uuid="df555d9f-30e6-47f9-9e9f-9814e6892857")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_wfc_lte_call_handover(self):
+        """ WFC to lte handover.
+
+        1. Make a voice call over wifi.
+        2. Turn off Wifi.
+        3. Call should handover from Wifi to LTE.
+        4. Verify call is active.
+        5. Hung up the call on PhoneA
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_volte, (self.log, ads[0])), (phone_setup_volte,
+                                                           (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            raise signals.TestFailure("Failed",
+                extras={"fail_reason": "Phone Failed to Set Up Properly."})
+
+
+        tasks = [(phone_setup_iwlan,
+                  (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
+                   self.wifi_network_ssid, self.wifi_network_pass))]
+
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            raise signals.TestFailure("Failed",
+                extras={"fail_reason": "Phone Failed to Set Up Properly."})
+
+        ad_caller = ads[0]
+        ad_callee = ads[1]
+        caller_number = get_phone_number(self.log, ad_caller)
+        callee_number = get_phone_number(self.log, ad_callee)
+
+        try:
+            # Make MO/MT call.
+            if not initiate_call(self.log, ad_caller, callee_number):
+                raise signals.TestFailure("Failed to initiate call")
+            if not wait_and_answer_call(self.log, ad_callee, caller_number):
+                raise signals.TestFailure("Answer call falied")
+            if not self._is_phone_in_call_iwlan():
+                raise signals.TestFailure("Phone call not in Iwlan ")
+            time.sleep(15)
+            # Turn off the wifi and wait call to handover.
+            wifi_toggle_state(self.log, self.android_devices[0], False)
+            time.sleep(15)
+            if not self.is_phone_in_call_volte():
+                raise signals.TestFailure("WFC handover failed, call disconnected ")
+
+            else:
+                self.log.info("Handover Successful")
+
+            if is_phone_in_call(self.log, ads[0]):
+                # hangup call
+                if not hangup_call(self.log, ads[0]):
+                    raise signals.TestFailure("hangup_call fail.")
+
+            else:
+                raise signals.TestFailure("Unexpected call drop.")
+
+
+        except Exception as e:
+                self.log.error("Exception error %s", str(e))
+                return False
+        return True
+
+    @test_tracker_info(uuid="331ff54c-ee36-4f59-9c3c-24faf41b1383")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_lte_wfc_call_handover(self):
+        """ LTE to WFC handover.
+
+        1. Make a voice call over LTE.
+        2. Turn on Wifi.
+        3. Call should handover from LTE to Wifi.
+        4. Verify call is active.
+        5. Hung up the call on PhoneA
+
+        Returns:
+            True if pass; False if fail.
+        """
+        ads = self.android_devices
+
+        tasks = [(phone_setup_volte, (self.log, ads[0])), (phone_setup_volte,
+                                                           (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            raise signals.TestFailure("Failed",
+                extras={"fail_reason": "Phone Failed to Set Up Properly."})
+
+
+        tasks = [(phone_setup_iwlan,
+                  (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED,
+                   self.wifi_network_ssid, self.wifi_network_pass))]
+
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up Properly.")
+            raise signals.TestFailure("Failed",
+                extras={"fail_reason": "Phone Failed to Set Up Properly."})
+
+        ad_caller = ads[0]
+        ad_callee = ads[1]
+        caller_number = get_phone_number(self.log, ad_caller)
+        callee_number = get_phone_number(self.log, ad_callee)
+
+        try:
+            # Turn off the wifi and make a call.
+            wifi_toggle_state(self.log, self.android_devices[0], False)
+            if not initiate_call(self.log, ad_caller, callee_number):
+                raise signals.TestFailure("Failed to initiate call")
+            if not wait_and_answer_call(self.log, ad_callee, caller_number):
+                raise signals.TestFailure("Answer call falied")
+            if not self.is_phone_in_call_volte():
+                raise signals.TestFailure("Phone call not via LTE")
+            time.sleep(15)
+            # Turn on the wifi and wait call to handover.
+            wifi_toggle_state(self.log, self.android_devices[0], True)
+            time.sleep(15)
+            if not self._is_phone_in_call_iwlan():
+                raise signals.TestFailure("LTE handover failed, call disconnected ")
+
+            else:
+                self.log.info("Handover Successful")
+
+            if is_phone_in_call(self.log, ads[0]):
+                # hangup call
+                if not hangup_call(self.log, ads[0]):
+                    raise signals.TestFailure("hangup_call fail.")
+
+            else:
+                raise signals.TestFailure("Unexpected call drop.")
+
+
+        except Exception as e:
+                self.log.error("Exception error %s", str(e))
+                return False
+        return True
+
+
 
 """ Tests End """
diff --git a/acts_tests/tests/google/wifi/SetupWifiNetworkTest.py b/acts_tests/tests/google/wifi/SetupWifiNetworkTest.py
index 26026ff..fa6084d 100644
--- a/acts_tests/tests/google/wifi/SetupWifiNetworkTest.py
+++ b/acts_tests/tests/google/wifi/SetupWifiNetworkTest.py
@@ -16,7 +16,6 @@
 
 import logging
 import socket
-import sys
 
 from acts import base_test
 from acts.controllers.ap_lib import hostapd_ap_preset
@@ -86,8 +85,8 @@
             "socket_port", "socket_timeout_secs"
         ]
         opt_params = []
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_params)
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_params)
         # Setup the AP environment
         self.setup_ap()
         # AP enviroment created. Wait for client to teardown the environment
@@ -99,8 +98,8 @@
             "socket_timeout_secs"
         ]
         opt_params = []
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_params)
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_params)
         # Setup the AP environment
         self.setup_ap()
         # AP enviroment created. Wait for client to teardown the environment
diff --git a/acts_tests/tests/google/wifi/WifiBridgedApTest.py b/acts_tests/tests/google/wifi/WifiBridgedApTest.py
index 34a7d32..93ae496 100644
--- a/acts_tests/tests/google/wifi/WifiBridgedApTest.py
+++ b/acts_tests/tests/google/wifi/WifiBridgedApTest.py
@@ -63,7 +63,7 @@
             raise signals.TestAbortClass("Legacy phone is not supported")
 
         req_params = ["dbs_supported_models"]
-        opt_param = ["cnss_diag_file", "pixel_models"]
+        opt_param = []
 
         self.unpack_userparams(
             req_param_names=req_params, opt_param_names=opt_param)
@@ -78,6 +78,8 @@
 
     def teardown_test(self):
         super().teardown_test()
+        # Reset unplugged status
+        self.dut.adb.shell("cmd battery reset")
         if self.dut.droid.wifiIsApEnabled():
             wutils.stop_wifi_tethering(self.dut)
         for ad in self.android_devices:
@@ -904,6 +906,8 @@
         asserts.skip_if(not is_supported, "BridgedAp is not supported in {}"
                         .format(wutils.WifiEnums.CountryCode.US))
 
+        # Simulate the unplugged scenario
+        self.dut.adb.shell("cmd battery unplug")
         # Enable BridgedAp and verify both 2G,5G instances have been enabled.
         self.enable_bridged_ap(self.dut,
                                WifiEnums.SoftApSecurityType.WPA3_SAE,
@@ -954,6 +958,8 @@
         asserts.skip_if(not is_supported, "BridgedAp is not supported in {}"
                         .format(wutils.WifiEnums.CountryCode.US))
 
+        # Simulate the unplugged scenario
+        self.dut.adb.shell("cmd battery unplug")
         # Enable BridgedAp and verify both 2G,5G instances have been enabled.
         self.enable_bridged_ap(self.dut,
                                WifiEnums.SoftApSecurityType.WPA3_SAE,
@@ -1028,6 +1034,8 @@
         asserts.skip_if(not is_supported, "BridgedAp is not supported in {}"
                         .format(wutils.WifiEnums.CountryCode.US))
 
+        # Simulate the unplugged scenario
+        self.dut.adb.shell("cmd battery unplug")
         # Enable BridgedAp and verify both 2G,5G instances have been enabled.
         self.enable_bridged_ap(self.dut,
                                WifiEnums.SoftApSecurityType.WPA3_SAE,
@@ -1129,6 +1137,8 @@
         asserts.skip_if(not is_supported, "BridgedAp is not supported in {}"
                         .format(wutils.WifiEnums.CountryCode.US))
 
+        # Simulate the unplugged scenario
+        self.dut.adb.shell("cmd battery unplug")
         # Enable BridgedAp and verify both 2G,5G instances have been enabled.
         self.enable_bridged_ap(self.dut,
                                WifiEnums.SoftApSecurityType.WPA3_SAE,
@@ -1173,6 +1183,8 @@
         asserts.skip_if(not is_supported, "BridgedAp is not supported in {}"
                         .format(wutils.WifiEnums.CountryCode.US))
 
+        # Simulate the unplugged scenario
+        self.dut.adb.shell("cmd battery unplug")
         # Enable BridgedAp and verify both 2G,5G instances have been enabled.
         self.enable_bridged_ap(self.dut,
                                WifiEnums.SoftApSecurityType.WPA3_SAE,
@@ -1223,6 +1235,8 @@
         asserts.skip_if(not is_supported, "BridgedAp is not supported in {}"
                         .format(wutils.WifiEnums.CountryCode.US))
 
+        # Simulate the unplugged scenario
+        self.dut.adb.shell("cmd battery unplug")
         # Enable BridgedAp and verify both 2G,5G instances have been enabled.
         self.enable_bridged_ap(self.dut,
                                WifiEnums.SoftApSecurityType.WPA3_SAE,
@@ -1272,6 +1286,9 @@
             self.dut, wutils.WifiEnums.CountryCode.US)
         asserts.skip_if(not is_supported, "BridgedAp is not supported in {}"
                         .format(wutils.WifiEnums.CountryCode.US))
+
+        # Simulate the unplugged scenario
+        self.dut.adb.shell("cmd battery unplug")
         # Enable BridgedAp with "Extend compatibility set to ON".
         self.enable_bridged_ap(self.dut,
                                WifiEnums.SoftApSecurityType.WPA3_SAE,
@@ -1314,6 +1331,9 @@
             self.dut, wutils.WifiEnums.CountryCode.US)
         asserts.skip_if(not is_supported, "BridgedAp is not supported in {}"
                         .format(wutils.WifiEnums.CountryCode.US))
+
+        # Simulate the unplugged scenario
+        self.dut.adb.shell("cmd battery unplug")
         # Enable BridgedAp with "Extend compatibility set to ON".
         self.enable_bridged_ap(self.dut,
                                WifiEnums.SoftApSecurityType.WPA3_SAE,
diff --git a/acts_tests/tests/google/wifi/WifiBtStressCoexTest.py b/acts_tests/tests/google/wifi/WifiBtStressCoexTest.py
index 41336c9..1799a6d 100644
--- a/acts_tests/tests/google/wifi/WifiBtStressCoexTest.py
+++ b/acts_tests/tests/google/wifi/WifiBtStressCoexTest.py
@@ -14,9 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import pprint
-import queue
-import threading
 import time
 
 import acts.base_test
@@ -33,6 +30,7 @@
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 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
+
 WifiEnums = wutils.WifiEnums
 
 WAIT_FOR_AUTO_CONNECT = 40
@@ -74,9 +72,10 @@
             "open_network", "reference_networks", "iperf_server_address",
             "stress_count", "stress_hours", "attn_vals", "pno_interval",
             "iperf_server_port", "dbs_supported_models",
-            "sta_sta_supported_models"]
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_param)
+            "sta_sta_supported_models"
+        ]
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_param)
 
         self.ap_iface = 'wlan0'
         if self.dut.model in self.dbs_supported_models:
@@ -143,8 +142,8 @@
 
         """
         ssid = network[WifiEnums.SSID_KEY]
-        wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut,
-                                                                   ssid)
+        wutils.start_wifi_connection_scan_and_ensure_network_found(
+            self.dut, ssid)
         wutils.wifi_connect_by_id(self.dut, net_id)
 
     def run_ping(self, sec):
@@ -156,7 +155,7 @@
         """
         self.log.info("Running ping for %d seconds" % sec)
         result = self.dut.adb.shell("ping -w %d %s" % (sec, PING_ADDR),
-                                    timeout=sec+1)
+                                    timeout=sec + 1)
         self.log.debug("Ping Result = %s" % result)
         if "100% packet loss" in result:
             raise signals.TestFailure("100% packet loss during ping")
@@ -184,10 +183,8 @@
             config
         """
         config = self.create_softap_config()
-        wutils.start_wifi_tethering(self.dut,
-                                    config[WifiEnums.SSID_KEY],
-                                    config[WifiEnums.PWD_KEY],
-                                    band)
+        wutils.start_wifi_tethering(self.dut, config[WifiEnums.SSID_KEY],
+                                    config[WifiEnums.PWD_KEY], band)
         for ad in self.android_devices[1:]:
             wutils.connect_to_wifi_network(
                 ad, config, check_connectivity=check_connectivity)
@@ -240,10 +237,17 @@
                 self.log.debug("WiFi was enabled on the device in %s s." %
                                startup_time)
             except:
-                raise signals.TestFailure(details="", extras={"Iterations": "%d" %
-                    self.stress_count, "Pass": "%d" % count})
-        raise signals.TestPass(details="", extras={"Iterations": "%d" %
-                    self.stress_count, "Pass": "%d" % (count+1)})
+                raise signals.TestFailure(details="",
+                                          extras={
+                                              "Iterations":
+                                              "%d" % self.stress_count,
+                                              "Pass": "%d" % count
+                                          })
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % (count + 1)
+                               })
 
     @test_tracker_info(uuid="ed5b77ff-8a64-4ea7-a481-96ad3a3b91d0")
     def test_wifi_on_off_with_5g(self):
@@ -266,10 +270,17 @@
                 self.log.debug("WiFi was enabled on the device in %s s." %
                                startup_time)
             except:
-                raise signals.TestFailure(details="", extras={"Iterations": "%d" %
-                    self.stress_count, "Pass": "%d" % count})
-        raise signals.TestPass(details="", extras={"Iterations": "%d" %
-                    self.stress_count, "Pass": "%d" % (count+1)})
+                raise signals.TestFailure(details="",
+                                          extras={
+                                              "Iterations":
+                                              "%d" % self.stress_count,
+                                              "Pass": "%d" % count
+                                          })
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % (count + 1)
+                               })
 
     @test_tracker_info(uuid="cc132d79-d0ea-4f99-ba6f-0f6fbd21eba0")
     def test_2g_sta_mode_and_hotspot_5g_on_off_stress_bt_paired(self):
@@ -282,7 +293,7 @@
         time.sleep(5)
         for count in range(self.stress_count):
             """Test toggling softap"""
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_softap_full_on_off(self.open_2g, WIFI_CONFIG_APBAND_5G)
 
     @test_tracker_info(uuid="de3903ec-9484-4302-814a-2027631d6ddb")
@@ -296,7 +307,7 @@
         time.sleep(5)
         for count in range(self.stress_count):
             """Test toggling softap"""
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_softap_full_on_off(self.open_2g, WIFI_CONFIG_APBAND_2G)
 
     @test_tracker_info(uuid="42a27ff4-aeb7-45f1-909b-e9695035fb95")
@@ -310,7 +321,7 @@
         time.sleep(5)
         for count in range(self.stress_count):
             """Test toggling softap"""
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_softap_full_on_off(self.open_2g, WIFI_CONFIG_APBAND_2G)
 
     @test_tracker_info(uuid="bae3c34b-34dd-47e4-8a6b-3b2dd97e169e")
@@ -324,7 +335,7 @@
         time.sleep(5)
         for count in range(self.stress_count):
             """Test toggling softap"""
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_softap_full_on_off(self.open_2g, WIFI_CONFIG_APBAND_5G)
 
     @test_tracker_info(uuid="4f39304b-18a1-4c54-8fa1-072ef3c1688d")
@@ -336,25 +347,28 @@
         time.sleep(5)
         for count in range(self.stress_count):
             """Test toggling softap"""
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             softap_config = wutils.create_softap_config()
             wutils.start_wifi_tethering(
-            self.dut, softap_config[wutils.WifiEnums.SSID_KEY],
-            softap_config[wutils.WifiEnums.PWD_KEY],
+                self.dut, softap_config[wutils.WifiEnums.SSID_KEY],
+                softap_config[wutils.WifiEnums.PWD_KEY],
                 wutils.WifiEnums.WIFI_CONFIG_APBAND_2G)
             config = {
                 "SSID": softap_config[wutils.WifiEnums.SSID_KEY],
                 "password": softap_config[wutils.WifiEnums.PWD_KEY]
             }
             wutils.wifi_toggle_state(self.dut_client, True)
-            wutils.connect_to_wifi_network(self.dut_client, config,
-                check_connectivity=False)
+            wutils.connect_to_wifi_network(self.dut_client,
+                                           config,
+                                           check_connectivity=False)
             # Ping the DUT
             dut_addr = self.dut.droid.connectivityGetIPv4Addresses(
                 self.ap_iface)[0]
             asserts.assert_true(
-                utils.adb_shell_ping(self.dut_client, count=10, dest_ip=dut_addr,
-                 timeout=20),
+                utils.adb_shell_ping(self.dut_client,
+                                     count=10,
+                                     dest_ip=dut_addr,
+                                     timeout=20),
                 "%s ping %s failed" % (self.dut_client.serial, dut_addr))
             wutils.wifi_toggle_state(self.dut_client, True)
             wutils.stop_wifi_tethering(self.dut)
@@ -368,25 +382,28 @@
         time.sleep(5)
         for count in range(self.stress_count):
             """Test toggling softap"""
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             softap_config = wutils.create_softap_config()
             wutils.start_wifi_tethering(
-            self.dut, softap_config[wutils.WifiEnums.SSID_KEY],
-            softap_config[wutils.WifiEnums.PWD_KEY],
+                self.dut, softap_config[wutils.WifiEnums.SSID_KEY],
+                softap_config[wutils.WifiEnums.PWD_KEY],
                 wutils.WifiEnums.WIFI_CONFIG_APBAND_5G)
             config = {
                 "SSID": softap_config[wutils.WifiEnums.SSID_KEY],
                 "password": softap_config[wutils.WifiEnums.PWD_KEY]
             }
             wutils.wifi_toggle_state(self.dut_client, True)
-            wutils.connect_to_wifi_network(self.dut_client, config,
-                check_connectivity=False)
+            wutils.connect_to_wifi_network(self.dut_client,
+                                           config,
+                                           check_connectivity=False)
             # Ping the DUT
             dut_addr = self.dut.droid.connectivityGetIPv4Addresses(
                 self.ap_iface)[0]
             asserts.assert_true(
-                utils.adb_shell_ping(self.dut_client, count=10, dest_ip=dut_addr,
-                 timeout=20),
+                utils.adb_shell_ping(self.dut_client,
+                                     count=10,
+                                     dest_ip=dut_addr,
+                                     timeout=20),
                 "%s ping %s failed" % (self.dut_client.serial, dut_addr))
             wutils.wifi_toggle_state(self.dut_client, True)
             wutils.stop_wifi_tethering(self.dut)
diff --git a/acts_tests/tests/google/wifi/WifiCellCoexChannelAvoidTest.py b/acts_tests/tests/google/wifi/WifiCellCoexChannelAvoidTest.py
index 6d47ddf..4f683b1 100644
--- a/acts_tests/tests/google/wifi/WifiCellCoexChannelAvoidTest.py
+++ b/acts_tests/tests/google/wifi/WifiCellCoexChannelAvoidTest.py
@@ -14,11 +14,8 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import os
 import time
-import re
 import json
-import logging
 import pprint
 
 from acts import asserts
@@ -31,7 +28,6 @@
 from acts_contrib.test_utils.wifi.wifi_constants import\
     COEX_BAND, COEX_CHANNEL, COEX_POWER_CAP_DBM, KEY_COEX_UNSAFE_CHANNELS, KEY_COEX_RESTRICTIONS
 
-
 WifiEnums = wutils.WifiEnums
 WIFI_CONFIG_APBAND_2G = WifiEnums.WIFI_CONFIG_APBAND_2G
 WIFI_CONFIG_APBAND_5G = WifiEnums.WIFI_CONFIG_APBAND_5G
@@ -44,7 +40,6 @@
 
 
 class WifiCellCoexChannelAvoidTest(WifiBaseTest):
-
     def __init__(self, configs):
         super().__init__(configs)
         self.generate_test_list()
@@ -54,8 +49,8 @@
             Test with a sorted list can reduce lots of time
             on switch radio and band start up.
         """
-        sorted_list = sorted(
-            self.user_params["coex_unsafe_list_sap"], key=lambda radio: radio["band"])
+        sorted_list = sorted(self.user_params["coex_unsafe_list_sap"],
+                             key=lambda radio: radio["band"])
         for test_item in sorted_list:
             self.init_test_case(self.coex_unsafechannel_avoidance, test_item)
         pprint.pprint("self.tests = {}".format(self.tests))
@@ -86,11 +81,13 @@
         super().setup_class()
         self.dut = self.android_devices[0]
         self.dut_client = self.android_devices[1]
-        req_params = ["dbs_supported_models", "sta_concurrency_supported_models",
-                      "wifi6_models","coex_unsafe_list_sap"]
+        req_params = [
+            "dbs_supported_models", "sta_concurrency_supported_models",
+            "wifi6_models", "coex_unsafe_list_sap"
+        ]
         opt_param = ["reference_networks"]
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_param)
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_param)
         if "AccessPoint" in self.user_params:
             self.legacy_configure_ap_and_start()
         elif "OpenWrtAP" in self.user_params:
@@ -106,10 +103,12 @@
         utils.sync_device_time(self.dut_client)
         # Enable verbose logging on the duts
         self.dut.droid.wifiEnableVerboseLogging(1)
-        asserts.assert_equal(self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
+        asserts.assert_equal(
+            self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
             "Failed to enable WiFi verbose logging on the softap dut.")
         self.dut_client.droid.wifiEnableVerboseLogging(1)
-        asserts.assert_equal(self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
+        asserts.assert_equal(
+            self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
             "Failed to enable WiFi verbose logging on the client dut.")
         wutils.wifi_toggle_state(self.dut, True)
         wutils.wifi_toggle_state(self.dut_client, True)
@@ -121,7 +120,8 @@
         if len(self.android_devices) > 2:
             utils.sync_device_time(self.android_devices[2])
             self.android_devices[2].droid.wifiEnableVerboseLogging(1)
-            asserts.assert_equal(self.android_devices[2].droid.wifiGetVerboseLoggingLevel(), 1,
+            asserts.assert_equal(
+                self.android_devices[2].droid.wifiGetVerboseLoggingLevel(), 1,
                 "Failed to enable WiFi verbose logging on the client dut.")
             self.dut_client_2 = self.android_devices[2]
 
@@ -151,8 +151,9 @@
         if self.dut.droid.wifiIsApEnabled():
             wutils.stop_wifi_tethering(self.dut)
         self.dut.log.debug("Toggling Airplane mode OFF.")
-        asserts.assert_true(utils.force_airplane_mode(self.dut, False),
-                            "Can not turn off airplane mode: %s" % self.dut.serial)
+        asserts.assert_true(
+            utils.force_airplane_mode(self.dut, False),
+            "Can not turn off airplane mode: %s" % self.dut.serial)
         #reset coexcell setting
         self.dut.adb.shell('cmd wifi reset-coex-cell-channels')
 
@@ -181,17 +182,17 @@
             wutils.save_wifi_soft_ap_config(
                 ad,
                 config,
-                bands=[WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G,
-                       WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G])
+                bands=[
+                    WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G,
+                    WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G
+                ])
         # If DUT does not support BridgedAp, 2G OR 5G SoftAp enabled.
         else:
             if self.init_softap_band == BAND_2G:
                 band = WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G
             if self.init_softap_band == BAND_5G:
                 band = WifiEnums.WIFI_CONFIG_SOFTAP_BAND_5G
-            wutils.save_wifi_soft_ap_config(ad,
-                                            config,
-                                            band=band)
+            wutils.save_wifi_soft_ap_config(ad, config, band=band)
         wutils.start_wifi_tethering_saved_config(ad)
         time.sleep(BRIDGED_AP_LAUNCH_INTERVAL_5_SECONDS)
 
@@ -204,15 +205,17 @@
             if len(infos) == 2:
                 freq_1 = infos[0]["frequency"]
                 freq_2 = infos[1]["frequency"]
-                self.dut.log.info("DUT connected to AP on freq: {},{}, chan: {} ,{}".
-                                  format(freq_1, freq_2, WifiEnums.freq_to_channel[freq_1],
-                                         WifiEnums.freq_to_channel[freq_2]))
+                self.dut.log.info(
+                    "DUT connected to AP on freq: {},{}, chan: {} ,{}".format(
+                        freq_1, freq_2, WifiEnums.freq_to_channel[freq_1],
+                        WifiEnums.freq_to_channel[freq_2]))
                 return freq_1, freq_2
             # if DUT BridgedAp has only one instances, return the frequency.
             elif len(infos) == 1:
                 freq = infos[0]["frequency"]
-                self.dut.log.info("DUT connected to AP on freq: {}, chan: {}".
-                                  format(freq, WifiEnums.freq_to_channel[freq]))
+                self.dut.log.info(
+                    "DUT connected to AP on freq: {}, chan: {}".format(
+                        freq, WifiEnums.freq_to_channel[freq]))
                 return freq
             else:
                 raise signals.TestFailure("There should be SoftAp instance.")
@@ -220,13 +223,13 @@
         else:
             # Return SoftAp frequency.
             callbackId = ad.droid.registerSoftApCallback()
-            freq, bandwidth = wutils.get_current_softap_info(ad,
-                                                             callbackId,
-                                                             True)
+            freq, bandwidth = wutils.get_current_softap_info(
+                ad, callbackId, True)
             ad.log.info("SoftAp freq: {}".format(freq))
             ad.droid.unregisterSoftApCallback(callbackId)
-            self.dut.log.info("DUT connected to AP on freq: {}, chan: {}".
-                        format(freq, WifiEnums.freq_to_channel[freq]))
+            self.dut.log.info(
+                "DUT connected to AP on freq: {}, chan: {}".format(
+                    freq, WifiEnums.freq_to_channel[freq]))
             return freq, bandwidth
 
     """ Tests Begin """
@@ -241,28 +244,34 @@
                         "Require SDK at least S to use wifi coex apis.")
         self.dut.ed.clear_all_events()
         #Listing the test coex setting from configuration
-        self.dut.log.info("DUT test cellcoex radio:{}, band:{}, channels setting:{}"
-                          .format(self.radio, self.band, self.cellchannels))
-        self.dut.adb.shell('cmd wifi set-coex-cell-channels %s %s %s' % (self.radio, self.band,
-                                                                         self.cellchannels))
+        self.dut.log.info(
+            "DUT test cellcoex radio:{}, band:{}, channels setting:{}".format(
+                self.radio, self.band, self.cellchannels))
+        self.dut.adb.shell('cmd wifi set-coex-cell-channels %s %s %s' %
+                           (self.radio, self.band, self.cellchannels))
         self.dut.droid.wifiRegisterCoexCallback()
         try:
             # Wait for the immediate callback from registering and store the current values
-            event = self.dut.ed.pop_event("WifiManagerCoexCallback#onCoexUnsafeChannelsChanged", 5)
+            event = self.dut.ed.pop_event(
+                "WifiManagerCoexCallback#onCoexUnsafeChannelsChanged", 5)
         except queue.Empty:
             asserts.fail("Coex callback event not received after registering.")
-        prev_unsafe_channels = sorted(json.loads(event["data"][KEY_COEX_UNSAFE_CHANNELS]),
+        prev_unsafe_channels = sorted(json.loads(
+            event["data"][KEY_COEX_UNSAFE_CHANNELS]),
                                       key=self.coex_unsafe_channel_key)
-        prev_restrictions = sorted(json.loads(event["data"][KEY_COEX_RESTRICTIONS]))
+        prev_restrictions = sorted(
+            json.loads(event["data"][KEY_COEX_RESTRICTIONS]))
         unsafe_channels = []
         for i in range(len(prev_unsafe_channels)):
             unsafe_channels.append(prev_unsafe_channels[i]['channel'])
         self.dut.log.info("DUT unsafe channels:{}".format(unsafe_channels))
         freq1, freq2 = self.enable_softap(self.dut)
-        sapchan1, sapchan2 = WifiEnums.freq_to_channel[freq1], WifiEnums.freq_to_channel[freq2]
+        sapchan1, sapchan2 = WifiEnums.freq_to_channel[
+            freq1], WifiEnums.freq_to_channel[freq2]
         if sapchan1 in unsafe_channels or sapchan2 in unsafe_channels:
-            asserts.fail("devices hotspot's channel open on current unsafe channels "
-                                + str(unsafe_channels))
+            asserts.fail(
+                "devices hotspot's channel open on current unsafe channels " +
+                str(unsafe_channels))
         else:
             pass
         self.dut.droid.wifiUnregisterCoexCallback()
@@ -270,5 +279,6 @@
 
     """ Tests End """
 
+
 if __name__ == "__main__":
     pass
diff --git a/acts_tests/tests/google/wifi/WifiCellStressTest.py b/acts_tests/tests/google/wifi/WifiCellStressTest.py
index eddb331..772bbf3 100644
--- a/acts_tests/tests/google/wifi/WifiCellStressTest.py
+++ b/acts_tests/tests/google/wifi/WifiCellStressTest.py
@@ -14,9 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import pprint
-import queue
-import threading
 import time
 
 import acts.base_test
@@ -30,6 +27,7 @@
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 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
+
 WifiEnums = wutils.WifiEnums
 
 DEFAULT_TIMEOUT = 10
@@ -59,10 +57,11 @@
             "open_network", "reference_networks", "iperf_server_address",
             "stress_count", "stress_hours", "attn_vals", "pno_interval",
             "iperf_server_port", "dbs_supported_models",
-            "sta_sta_supported_models"]
+            "sta_sta_supported_models"
+        ]
 
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_param)
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_param)
         self.ap_iface = 'wlan0'
         if self.dut.model in self.dbs_supported_models:
             self.ap_iface = 'wlan1'
@@ -129,7 +128,7 @@
         """
         self.log.info("Running ping for %d seconds" % sec)
         result = self.dut.adb.shell("ping -w %d %s" % (sec, PING_ADDR),
-                                    timeout=sec+1)
+                                    timeout=sec + 1)
         self.log.debug("Ping Result = %s" % result)
         if "100% packet loss" in result:
             raise signals.TestFailure("100% packet loss during ping")
@@ -152,12 +151,11 @@
         time.sleep(DEFAULT_TIMEOUT)
         for count in range(self.stress_count):
             """Test toggling softap"""
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             softap_config = wutils.create_softap_config()
-            wutils.start_wifi_tethering(self.dut,
-                softap_config[wutils.WifiEnums.SSID_KEY],
-                softap_config[wutils.WifiEnums.PWD_KEY],
-                band)
+            wutils.start_wifi_tethering(
+                self.dut, softap_config[wutils.WifiEnums.SSID_KEY],
+                softap_config[wutils.WifiEnums.PWD_KEY], band)
             config = {
                 "SSID": softap_config[wutils.WifiEnums.SSID_KEY],
                 "password": softap_config[wutils.WifiEnums.PWD_KEY]
@@ -169,22 +167,26 @@
             "Can not turn off airplane mode on: %s" % self.dut.serial)
         self.check_cell_data_and_enable()
         softap_config = wutils.create_softap_config()
-        wutils.start_wifi_tethering(
-        self.dut, softap_config[wutils.WifiEnums.SSID_KEY],
-        softap_config[wutils.WifiEnums.PWD_KEY], band)
+        wutils.start_wifi_tethering(self.dut,
+                                    softap_config[wutils.WifiEnums.SSID_KEY],
+                                    softap_config[wutils.WifiEnums.PWD_KEY],
+                                    band)
         config = {
             "SSID": softap_config[wutils.WifiEnums.SSID_KEY],
             "password": softap_config[wutils.WifiEnums.PWD_KEY]
         }
         wutils.wifi_toggle_state(self.dut_client, True)
-        wutils.connect_to_wifi_network(self.dut_client, config,
-            check_connectivity=False)
+        wutils.connect_to_wifi_network(self.dut_client,
+                                       config,
+                                       check_connectivity=False)
         # Ping the DUT
         dut_addr = self.dut.droid.connectivityGetIPv4Addresses(
             self.ap_iface)[0]
         asserts.assert_true(
-            utils.adb_shell_ping(self.dut_client, count=10, dest_ip=dut_addr,
-                 timeout=20),
+            utils.adb_shell_ping(self.dut_client,
+                                 count=10,
+                                 dest_ip=dut_addr,
+                                 timeout=20),
             "%s ping %s failed" % (self.dut_client.serial, dut_addr))
         wutils.wifi_toggle_state(self.dut_client, True)
         wutils.stop_wifi_tethering(self.dut)
diff --git a/acts_tests/tests/google/wifi/WifiChaosTest.py b/acts_tests/tests/google/wifi/WifiChaosTest.py
index 1056dd6..d2aad85 100755
--- a/acts_tests/tests/google/wifi/WifiChaosTest.py
+++ b/acts_tests/tests/google/wifi/WifiChaosTest.py
@@ -15,7 +15,6 @@
 #   limitations under the License.
 
 import re
-import sys
 import random
 import time
 
@@ -99,8 +98,8 @@
         hostname = ssid_dict[testcase_name]['host']
         if not testcase_name.startswith('test_'):
             testcase_name = 'test_%s' % testcase_name
-        test_case = test_tracker_info(uuid=test_tracker_uuid)(
-            lambda: base_test(ssid, hostname))
+        test_case = test_tracker_info(
+            uuid=test_tracker_uuid)(lambda: base_test(ssid, hostname))
         setattr(self, testcase_name, test_case)
         self.tests.append(testcase_name)
 
@@ -140,7 +139,8 @@
                 if dutils.lock_device(device_name, self.admin):
                     self.pcap_host = device_name
                     host = device['ip_address']
-                    self.log.info("Locked Packet Capture device: %s" % device_name)
+                    self.log.info("Locked Packet Capture device: %s" %
+                                  device_name)
                     locked_pcap = True
                     break
                 else:
@@ -149,7 +149,7 @@
         if not locked_pcap:
             return False
 
-        pcap_config = {'ssh_config':{'user':'root'} }
+        pcap_config = {'ssh_config': {'user': 'root'}}
         pcap_config['ssh_config']['host'] = host
 
         self.pcap = packet_capture.PacketCapture(pcap_config)
@@ -179,7 +179,6 @@
         if not dutils.unlock_device(self.pcap_host):
             self.log.warning("Failed to unlock %s PCAP. Check in datastore.")
 
-
     """Helper Functions"""
 
     def scan_and_connect_by_id(self, network, net_id):
@@ -190,8 +189,8 @@
 
         """
         ssid = network[WifiEnums.SSID_KEY]
-        wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut,
-                                                                   ssid)
+        wutils.start_wifi_connection_scan_and_ensure_network_found(
+            self.dut, ssid)
         wutils.wifi_connect_by_id(self.dut, net_id)
 
     def run_ping(self, sec):
@@ -222,10 +221,10 @@
         2. Ensure that the device and AP did not crash (by checking that the
            device remains connected to the expected network).
         """
-        results = wutils.send_link_probes(
-            self.dut, NUM_LINK_PROBES, PROBE_DELAY_SEC)
+        results = wutils.send_link_probes(self.dut, NUM_LINK_PROBES,
+                                          PROBE_DELAY_SEC)
 
-        self.log.info("Link Probe results: %s" % (results,))
+        self.log.info("Link Probe results: %s" % (results, ))
 
         wifi_info = self.dut.droid.wifiGetConnectionInfo()
         expected = network[WifiEnums.SSID_KEY]
@@ -269,7 +268,8 @@
                 begin_time = time.time()
                 ssid = network[WifiEnums.SSID_KEY]
                 net_id = self.dut.droid.wifiAddNetwork(network)
-                asserts.assert_true(net_id != -1, "Add network %s failed" % network)
+                asserts.assert_true(net_id != -1,
+                                    "Add network %s failed" % network)
                 self.log.info("Connecting to %s" % ssid)
                 self.scan_and_connect_by_id(network, net_id)
                 self.run_ping(10)
@@ -279,7 +279,8 @@
                 time.sleep(WAIT_BEFORE_CONNECTION)
             except Exception as e:
                 self.log.error("Connection to %s network failed on the %d "
-                               "attempt with exception %s." % (ssid, attempt, e))
+                               "attempt with exception %s." %
+                               (ssid, attempt, e))
                 # TODO:(bmahadev) Uncomment after scan issue is fixed.
                 # self.dut.take_bug_report(ssid, begin_time)
                 # self.dut.cat_adb_log(ssid, begin_time)
@@ -299,7 +300,7 @@
         for item in ssid_info:
             # Skip over the router model part.
             if 'ch' in item and item != ssid_info[0]:
-                self.chan = re.search(r'(\d+)',item).group(0)
+                self.chan = re.search(r'(\d+)', item).group(0)
                 return
         raise signals.TestFailure("Channel information not found in SSID.")
 
@@ -341,8 +342,7 @@
         band = SINGLE_BAND
         if ('ssid_2g' in ap_info) and ('ssid_5g' in ap_info):
             band = DUAL_BAND
-        if (band == SINGLE_BAND) or (
-                band == DUAL_BAND and '5G' in ssid):
+        if (band == SINGLE_BAND) or (band == DUAL_BAND and '5G' in ssid):
             release_ap = True
 
         # Get AP RPM attributes and Turn ON AP.
@@ -356,8 +356,8 @@
 
         self.get_band_and_chan(ssid)
         self.pcap.configure_monitor_mode(self.band, self.chan)
-        self.pcap_procs = wutils.start_pcap(
-                self.pcap, self.band.lower(), self.test_name)
+        self.pcap_procs = wutils.start_pcap(self.pcap, self.band.lower(),
+                                            self.test_name)
         self.run_connect_disconnect(network, hostname, rpm_port, rpm_ip,
                                     release_ap)
 
diff --git a/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py b/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py
index 28dc90e..1f5df4d 100644
--- a/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py
+++ b/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py
@@ -15,8 +15,6 @@
 #   limitations under the License.
 
 import itertools
-import pprint
-import queue
 import time
 
 import acts.base_test
@@ -33,9 +31,9 @@
 # Default timeout used for reboot, toggle WiFi and Airplane mode,
 # for the system to settle down after the operation.
 DEFAULT_TIMEOUT = 10
-GET_MAC_ADDRESS= ("ip addr show wlan0"
-                  "| grep 'link/ether'"
-                  "| cut -d ' ' -f6")
+GET_MAC_ADDRESS = ("ip addr show wlan0"
+                   "| grep 'link/ether'"
+                   "| cut -d ' ' -f6")
 MAC_SETTING = "wifi_connected_mac_randomization_enabled"
 GET_MAC_RANDOMIZATION_STATUS = "settings get global {}".format(MAC_SETTING)
 TURN_ON_MAC_RANDOMIZATION = "settings put global {} 1".format(MAC_SETTING)
@@ -43,6 +41,7 @@
 LOG_CLEAR = "logcat -c"
 LOG_GREP = "logcat -d | grep {}"
 
+
 class WifiConnectedMacRandomizationTest(WifiBaseTest):
     """Tests for Connected MAC Randomization.
 
@@ -67,8 +66,8 @@
 
         req_params = ["reference_networks"]
         opt_param = []
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_param)
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_param)
 
         if "AccessPoint" in self.user_params:
             self.legacy_configure_ap_and_start()
@@ -104,6 +103,7 @@
             del self.user_params["open_network"]
 
     """Helper Functions"""
+
     def get_current_mac_address(self, ad):
         """Get the device's wlan0 MAC address.
 
@@ -158,6 +158,7 @@
         return ssid_id_dict
 
     """Tests"""
+
     @test_tracker_info(uuid="")
     def test_wifi_connection_2G_with_mac_randomization(self):
         """Tests connection to 2G network with Connected MAC Randomization.
@@ -200,29 +201,25 @@
             "Randomized MAC addresses for 2G and 5G networks are equal.")
 
         reconnect_2g = wutils.connect_to_wifi_network_with_id(
-            self.dut,
-            connect_data_2g[WifiEnums.NETID_KEY],
+            self.dut, connect_data_2g[WifiEnums.NETID_KEY],
             connect_data_2g[WifiEnums.SSID_KEY])
         if not reconnect_2g:
             raise signals.TestFailure("Device did not connect to the correct"
                                       " 2GHz network.")
         new_mac_2g = self.get_current_mac_address(self.dut)
         asserts.assert_equal(
-            old_mac_2g,
-            new_mac_2g,
+            old_mac_2g, new_mac_2g,
             "Randomized MAC for 2G is not persistent between connections.")
 
         reconnect_5g = wutils.connect_to_wifi_network_with_id(
-            self.dut,
-            connect_data_5g[WifiEnums.NETID_KEY],
+            self.dut, connect_data_5g[WifiEnums.NETID_KEY],
             connect_data_5g[WifiEnums.SSID_KEY])
         if not reconnect_5g:
             raise signals.TestFailure("Device did not connect to the correct"
                                       " 5GHz network.")
         new_mac_5g = self.get_current_mac_address(self.dut)
         asserts.assert_equal(
-            old_mac_5g,
-            new_mac_5g,
+            old_mac_5g, new_mac_5g,
             "Randomized MAC for 5G is not persistent between connections.")
 
     @test_tracker_info(uuid="")
diff --git a/acts_tests/tests/google/wifi/WifiCountrySoftApAcsTest.py b/acts_tests/tests/google/wifi/WifiCountrySoftApAcsTest.py
index 0f7dd3f..fe80116 100644
--- a/acts_tests/tests/google/wifi/WifiCountrySoftApAcsTest.py
+++ b/acts_tests/tests/google/wifi/WifiCountrySoftApAcsTest.py
@@ -55,7 +55,7 @@
             self.openwrt.verify_wifi_status(timeout=60)
 
         req_params = []
-        opt_param = ["cnss_diag_file", "pixel_models"]
+        opt_param = []
 
         self.unpack_userparams(
             req_param_names=req_params, opt_param_names=opt_param)
diff --git a/acts_tests/tests/google/wifi/WifiCrashTest.py b/acts_tests/tests/google/wifi/WifiCrashTest.py
index a8b0db6..d8ba8f6 100644
--- a/acts_tests/tests/google/wifi/WifiCrashTest.py
+++ b/acts_tests/tests/google/wifi/WifiCrashTest.py
@@ -15,8 +15,6 @@
 #   limitations under the License.
 
 import itertools
-import pprint
-import queue
 import time
 
 import acts.base_test
@@ -39,6 +37,7 @@
 WIFI_VENDOR_HAL_DAEMON = "android.hardware.wifi@1.0-service"
 WIFI_VENDOR_HAL_DAEMON_KILL_SHELL_COMMAND = "killall android.hardware.wifi@1.0-service"
 
+
 class WifiCrashTest(WifiBaseTest):
     """Crash Tests for wifi stack.
 
@@ -46,6 +45,7 @@
     * One Android device
     * One Wi-Fi network visible to the device.
     """
+
     def __init__(self, configs):
         super().__init__(configs)
         self.enable_packet_log = True
@@ -57,8 +57,8 @@
         wutils.wifi_test_device_init(self.dut)
         req_params = []
         opt_param = ["reference_networks"]
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_param)
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_param)
 
         if "AccessPoint" in self.user_params:
             self.legacy_configure_ap_and_start()
@@ -87,8 +87,8 @@
             del self.user_params["reference_networks"]
 
     """Helper Functions"""
-
     """Tests"""
+
     @test_tracker_info(uuid="b87fd23f-9bfc-406b-a5b2-17ce6be6c780")
     def test_wifi_framework_crash_reconnect(self):
         """Connect to a network, crash framework, then ensure
@@ -185,5 +185,6 @@
         time.sleep(RECOVERY_TIMEOUT)
         wifi_info = self.dut.droid.wifiGetConnectionInfo()
         if wifi_info[WifiEnums.SSID_KEY] != self.network[WifiEnums.SSID_KEY]:
-            raise signals.TestFailure("Device did not connect to the"
-                                      " network after crashing wpa_supplicant.")
+            raise signals.TestFailure(
+                "Device did not connect to the"
+                " network after crashing wpa_supplicant.")
diff --git a/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py b/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py
index a166b9d..7271f1b 100644
--- a/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py
+++ b/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py
@@ -15,8 +15,6 @@
 #   limitations under the License.
 
 import itertools
-import pprint
-import queue
 import time
 
 import acts.base_test
@@ -48,15 +46,14 @@
         wutils.wifi_test_device_init(self.dut)
         req_params = []
         opt_param = ["open_network"]
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_param)
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_param)
 
         if "AccessPoint" in self.user_params:
             self.legacy_configure_ap_and_start()
         wutils.wifi_toggle_state(self.dut, True)
         asserts.assert_true(
-            len(self.open_network) > 0,
-            "Need at least one open network.")
+            len(self.open_network) > 0, "Need at least one open network.")
         self.open_network = self.open_network[0]["2g"]
 
     def setup_test(self):
@@ -93,6 +90,8 @@
             """ Gets this error because adb.shell trys to parse the output to a string
             but ringbuffer dumps should already be generated """
             self.log.info("Unicode decode error occurred, but this is ok")
-        file_count_plus_one = self.dut.adb.shell("ls -l data/vendor/tombstones/wifi | wc -l")
+        file_count_plus_one = self.dut.adb.shell(
+            "ls -l data/vendor/tombstones/wifi | wc -l")
         if int(file_count_plus_one) <= 1:
-            raise signals.TestFailure("Failed to create ringbuffer debug files.")
\ No newline at end of file
+            raise signals.TestFailure(
+                "Failed to create ringbuffer debug files.")
diff --git a/acts_tests/tests/google/wifi/WifiIOTConnectionTest.py b/acts_tests/tests/google/wifi/WifiIOTConnectionTest.py
index 98d9e01..382c4de 100644
--- a/acts_tests/tests/google/wifi/WifiIOTConnectionTest.py
+++ b/acts_tests/tests/google/wifi/WifiIOTConnectionTest.py
@@ -60,6 +60,7 @@
     def setup_test(self):
         self.dut.droid.wakeLockAcquireBright()
         self.dut.droid.wakeUpNow()
+        wutils.reset_wifi(self.dut)
 
     def teardown_test(self):
         self.dut.droid.wakeLockRelease()
@@ -165,11 +166,6 @@
                           .format(self.user_params["pdu_wait_time"]))
             time.sleep(self.user_params["pdu_wait_time"])
 
-    def scan_wifi_list(self, network, test_item):
-        wutils.reset_wifi(self.dut)
-        scan_results = self.dut.droid.wifiGetScanResults()
-        return scan_results
-
     def start_packet_capture(self, band, channel):
         """Configure wireless packet capture on pre-defined channels.
 
@@ -197,24 +193,26 @@
             self.dut.log.error('Faild to ping public gateway 8.8.8.8')
             return False
 
-    def connect_to_network_and_ping(self, network, scan_results):
+    def connect_to_network_and_ping(self, network):
         ssid = network[WifiEnums.SSID_KEY]
         connection_pass = 0
         for i in range(self.user_params['iot_connection_iteration']):
             self.dut.log.info('Connection iteration : {}'.format(i + 1))
-            for ap in scan_results:
-                if ap['SSID'] == ssid:
-                    wutils.wifi_connect(self.dut, network, num_of_tries=1,
-                                        check_connectivity=False)
-                    time.sleep(WAIT_BETWEEN_ACTIONS)
-                    self.rssi, self.link_speed, self.freq = self.get_wifi_info()
-                    time.sleep(WAIT_BETWEEN_ACTIONS)
-                    if self.ping_public_gateway_ip():
-                        connection_pass += 1
-                    wutils.wifi_forget_network(self.dut, ssid)
-                    self.dut.log.info("connection_pass: {}"
-                                      .format(connection_pass))
-                    time.sleep(WAIT_BETWEEN_ACTIONS)
+            try:
+                wutils.connect_to_wifi_network(self.dut, network,
+                        num_of_connect_tries=1,
+                        check_connectivity=False)
+                time.sleep(WAIT_BETWEEN_ACTIONS)
+                self.rssi, self.link_speed, self.freq = self.get_wifi_info()
+                time.sleep(WAIT_BETWEEN_ACTIONS)
+                if self.ping_public_gateway_ip():
+                    connection_pass += 1
+                wutils.wifi_forget_network(self.dut, ssid)
+                self.dut.log.info("connection_pass: {}"
+                                    .format(connection_pass))
+                time.sleep(WAIT_BETWEEN_ACTIONS)
+            except:
+                self.dut.log.error("connection_fail")
 
         # Create a dictionary to store data in a json file.
         connection_result = {}
@@ -282,8 +280,5 @@
         if hasattr(self, 'packet_capture'):
             self.start_packet_capture(self.band, self.channel)
 
-        # Scan Wi-Fi SSIDs once.
-        wifi_scan_list = self.scan_wifi_list(network, test_item)
-
         # Connect to Wi-Fi network and ping public gateway for 5 times.
-        self.connect_to_network_and_ping(network, wifi_scan_list)
+        self.connect_to_network_and_ping(network)
diff --git a/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py b/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py
index d83460d..a71f83c 100644
--- a/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py
+++ b/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py
@@ -15,8 +15,6 @@
 #   limitations under the License.
 
 import itertools
-import pprint
-import queue
 import re
 import time
 
@@ -66,13 +64,13 @@
         self.dut_client = self.android_devices[1]
         wutils.wifi_test_device_init(self.dut)
         wutils.wifi_test_device_init(self.dut_client)
-        req_params = ["sta_sta_supported_models", "dbs_supported_models",
-                      "support_one_factory_mac_address", "roaming_attn"]
-        opt_param = [
-            "open_network", "reference_networks", "wep_networks"
+        req_params = [
+            "sta_sta_supported_models", "dbs_supported_models",
+            "support_one_factory_mac_address", "roaming_attn"
         ]
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_param)
+        opt_param = ["open_network", "reference_networks", "wep_networks"]
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_param)
 
         if not hasattr(self, 'packet_capture'):
             raise signals.TestFailure("Needs packet_capture attribute to "
@@ -80,8 +78,7 @@
         self.configure_packet_capture()
 
         if "AccessPoint" in self.user_params:
-            self.legacy_configure_ap_and_start(wep_network=True,
-                                               ap_count=2)
+            self.legacy_configure_ap_and_start(wep_network=True, ap_count=2)
         elif "OpenWrtAP" in self.user_params:
             self.configure_openwrt_ap_and_start(open_network=True,
                                                 wpa_network=True,
@@ -100,8 +97,8 @@
         wutils.wifi_toggle_state(self.dut, True)
         wutils.wifi_toggle_state(self.dut_client, True)
         if self.dut.model in self.support_one_factory_mac_address:
-            self.soft_ap_factory_mac = (self.dut.droid
-                                        .wifigetFactorymacAddresses()[0])
+            self.soft_ap_factory_mac = (
+                self.dut.droid.wifigetFactorymacAddresses()[0])
         else:
             self.soft_ap_factory_mac = self.get_soft_ap_mac_address()
         self.sta_factory_mac = self.dut.droid.wifigetFactorymacAddresses()[0]
@@ -135,10 +132,8 @@
             del self.user_params["open_network"]
             del self.user_params["wep_networks"]
 
-
     """Helper Functions"""
 
-
     def get_randomized_mac(self, network):
         """Get the randomized MAC address.
 
@@ -151,8 +146,8 @@
         """
         return self.dut.droid.wifigetRandomizedMacAddress(network)
 
-    def connect_to_network_and_verify_mac_randomization(self, network,
-            status=RANDOMIZATION_PERSISTENT):
+    def connect_to_network_and_verify_mac_randomization(
+            self, network, status=RANDOMIZATION_PERSISTENT):
         """Connect to the given network and verify MAC.
 
           Args:
@@ -176,13 +171,15 @@
 
         """
         rand_mac = self.connect_to_network_and_verify_mac_randomization(
-                network)
+            network)
         if rand_mac in mac_list:
             raise signals.TestFailure('A new Randomized MAC was not generated '
                                       ' for this network %s.' % network)
         mac_list.append(rand_mac)
 
-    def verify_mac_randomization(self, network, status=RANDOMIZATION_PERSISTENT):
+    def verify_mac_randomization(self,
+                                 network,
+                                 status=RANDOMIZATION_PERSISTENT):
         """Get the various types of MAC addresses for the device and verify.
 
         Args:
@@ -195,17 +192,22 @@
         """
         randomized_mac = self.get_randomized_mac(network)
         default_mac = self.get_sta_mac_address()
-        self.log.info("Factory MAC = %s\nRandomized MAC = %s\nDefault MAC = %s" %
-              (self.sta_factory_mac, randomized_mac, default_mac))
+        self.log.info(
+            "Factory MAC = %s\nRandomized MAC = %s\nDefault MAC = %s" %
+            (self.sta_factory_mac, randomized_mac, default_mac))
         message = ('Randomized MAC and Factory MAC are the same. '
-                   'Randomized MAC = %s, Factory MAC = %s' % (randomized_mac, self.sta_factory_mac))
+                   'Randomized MAC = %s, Factory MAC = %s' %
+                   (randomized_mac, self.sta_factory_mac))
         asserts.assert_true(randomized_mac != self.sta_factory_mac, message)
         if status == RANDOMIZATION_NONE:
-            asserts.assert_true(default_mac == self.sta_factory_mac, "Connection is not "
+            asserts.assert_true(
+                default_mac == self.sta_factory_mac, "Connection is not "
                 "using Factory MAC as the default MAC.")
         else:
-            message = ('Connection is not using randomized MAC as the default MAC. '
-                       'Randomized MAC = %s, Deafult MAC = %s' % (randomized_mac, default_mac))
+            message = (
+                'Connection is not using randomized MAC as the default MAC. '
+                'Randomized MAC = %s, Default MAC = %s' %
+                (randomized_mac, default_mac))
             asserts.assert_true(default_mac == randomized_mac, message)
         return randomized_mac
 
@@ -221,7 +223,8 @@
             TestFaikure is the MAC is not persistent.
 
         """
-        rand_mac1 = self.connect_to_network_and_verify_mac_randomization(network)
+        rand_mac1 = self.connect_to_network_and_verify_mac_randomization(
+            network)
 
         if condition == FORGET:
             wutils.wifi_forget_network(self.dut, network['SSID'])
@@ -240,7 +243,8 @@
             wutils.turn_ap_on(self, 1)
             time.sleep(DEFAULT_TIMEOUT)
 
-        rand_mac2 = self.connect_to_network_and_verify_mac_randomization(network)
+        rand_mac2 = self.connect_to_network_and_verify_mac_randomization(
+            network)
 
         if rand_mac1 != rand_mac2:
             raise signals.TestFailure('Randomized MAC is not persistent after '
@@ -251,9 +255,9 @@
         for pkt in packets:
             self.log.debug("Packet Summary = %s" % pkt.summary())
             if mac in pkt.summary():
-                raise signals.TestFailure("Caught Factory MAC in packet sniffer"
-                                          "Packet = %s Device = %s"
-                                           % (pkt.show(), self.dut))
+                raise signals.TestFailure(
+                    "Caught Factory MAC in packet sniffer"
+                    "Packet = %s Device = %s" % (pkt.show(), self.dut))
 
     def verify_mac_is_found_in_pcap(self, mac, packets):
         for pkt in packets:
@@ -295,11 +299,13 @@
             "Failed to add suggestions")
         wutils.start_wifi_connection_scan_and_ensure_network_found(
             self.dut, network_suggestion[WifiEnums.SSID_KEY])
-        wutils.wait_for_connect(self.dut, network_suggestion[WifiEnums.SSID_KEY])
+        wutils.wait_for_connect(self.dut,
+                                network_suggestion[WifiEnums.SSID_KEY])
         default_mac = self.get_sta_mac_address()
         randomized_mac = self.dut.droid.wifiGetConnectionInfo()["mac_address"]
-        self.log.info("Factory MAC = %s\nRandomized MAC = %s\nDefault MAC = %s" %
-                      (self.sta_factory_mac, randomized_mac, default_mac))
+        self.log.info(
+            "Factory MAC = %s\nRandomized MAC = %s\nDefault MAC = %s" %
+            (self.sta_factory_mac, randomized_mac, default_mac))
         asserts.assert_true(
             default_mac == randomized_mac,
             "Connection is not using randomized MAC as the default MAC.")
@@ -318,24 +324,22 @@
         wutils.wait_for_disconnect(self.dut)
         self.dut.ed.clear_all_events()
         asserts.assert_false(
-            wutils.wait_for_connect(
-                self.dut,
-                network_suggestion[WifiEnums.SSID_KEY],
-                assert_on_fail=False),
+            wutils.wait_for_connect(self.dut,
+                                    network_suggestion[WifiEnums.SSID_KEY],
+                                    assert_on_fail=False),
             "Device should not connect back")
 
     """Tests"""
 
-
     @test_tracker_info(uuid="2dd0a05e-a318-45a6-81cd-962e098fa242")
     def test_set_mac_randomization_to_none(self):
-        self.pcap_procs = wutils.start_pcap(
-            self.packet_capture, 'dual', self.test_name)
+        self.pcap_procs = wutils.start_pcap(self.packet_capture, 'dual',
+                                            self.test_name)
         network = self.wpapsk_2g
         # Set macRandomizationSetting to RANDOMIZATION_NONE.
         network["macRand"] = RANDOMIZATION_NONE
-        self.connect_to_network_and_verify_mac_randomization(network,
-            status=RANDOMIZATION_NONE)
+        self.connect_to_network_and_verify_mac_randomization(
+            network, status=RANDOMIZATION_NONE)
         pcap_fname = '%s_%s.pcap' % \
             (self.pcap_procs[hostapd_constants.BAND_2G][1],
              hostapd_constants.BAND_2G.upper())
@@ -452,15 +456,18 @@
 
         # Connect to the previous network and check MAC is persistent.
         mac_wpapsk = self.connect_to_network_and_verify_mac_randomization(
-                self.wpapsk_2g)
-        msg = ('Randomized MAC is not persistent for this network %s. Old MAC = '
-               '%s \nNew MAC = %s')
+            self.wpapsk_2g)
+        msg = (
+            'Randomized MAC is not persistent for this network %s. Old MAC = '
+            '%s \nNew MAC = %s')
         if mac_wpapsk != mac_list[0]:
-            raise signals.TestFailure(msg % (self.wpapsk_5g, mac_list[0], mac_wpapsk))
+            raise signals.TestFailure(
+                msg % (self.wpapsk_5g, mac_list[0], mac_wpapsk))
         mac_open = self.connect_to_network_and_verify_mac_randomization(
-                self.open_2g)
+            self.open_2g)
         if mac_open != mac_list[1]:
-            raise signals.TestFailure(msg %(self.open_5g, mac_list[1], mac_open))
+            raise signals.TestFailure(msg %
+                                      (self.open_5g, mac_list[1], mac_open))
 
     @test_tracker_info(uuid="edb5a0e5-7f3b-4147-b1d3-48ad7ad9799e")
     def test_mac_randomization_different_APs(self):
@@ -477,8 +484,9 @@
         mac_ap1 = self.connect_to_network_and_verify_mac_randomization(ap1)
         mac_ap2 = self.connect_to_network_and_verify_mac_randomization(ap2)
         if mac_ap1 == mac_ap2:
-            raise signals.TestFailure("Same MAC address was generated for both "
-                                      "APs: %s" % mac_ap1)
+            raise signals.TestFailure(
+                "Same MAC address was generated for both "
+                "APs: %s" % mac_ap1)
 
     @test_tracker_info(uuid="b815e9ce-bccd-4fc3-9774-1e1bc123a2a8")
     def test_mac_randomization_ap_sta(self):
@@ -495,9 +503,10 @@
 
         """
         wutils.set_wifi_country_code(self.dut, wutils.WifiEnums.CountryCode.US)
-        wutils.set_wifi_country_code(self.dut_client, wutils.WifiEnums.CountryCode.US)
+        wutils.set_wifi_country_code(self.dut_client,
+                                     wutils.WifiEnums.CountryCode.US)
         mac_sta = self.connect_to_network_and_verify_mac_randomization(
-                self.wpapsk_2g)
+            self.wpapsk_2g)
         softap = wutils.start_softap_and_verify(self, WIFI_CONFIG_APBAND_2G)
         wutils.connect_to_wifi_network(self.dut_client, softap)
         softap_info = self.dut_client.droid.wifiGetConnectionInfo()
@@ -508,19 +517,21 @@
 
         # Verify SoftAp MAC is randomized
         softap_mac = self.get_soft_ap_mac_address()
-        message = ('Randomized SoftAp MAC and Factory SoftAp MAC are the same. '
-                   'Randomized SoftAp MAC = %s, Factory SoftAp MAC = %s'
-                   % (softap_mac, self.soft_ap_factory_mac))
+        message = (
+            'Randomized SoftAp MAC and Factory SoftAp MAC are the same. '
+            'Randomized SoftAp MAC = %s, Factory SoftAp MAC = %s' %
+            (softap_mac, self.soft_ap_factory_mac))
         asserts.assert_true(softap_mac != self.soft_ap_factory_mac, message)
 
-        softap_channel = hostapd_constants.CHANNEL_MAP[softap_info['frequency']]
+        softap_channel = hostapd_constants.CHANNEL_MAP[
+            softap_info['frequency']]
         self.log.info("softap_channel = %s\n" % (softap_channel))
         result = self.packet_capture.configure_monitor_mode(
             hostapd_constants.BAND_2G, softap_channel)
         if not result:
             raise ValueError("Failed to configure channel for 2G band")
-        self.pcap_procs = wutils.start_pcap(
-            self.packet_capture, 'dual', self.test_name)
+        self.pcap_procs = wutils.start_pcap(self.packet_capture, 'dual',
+                                            self.test_name)
         # re-connect to the softAp network after sniffer is started
         wutils.connect_to_wifi_network(self.dut_client, self.wpapsk_2g)
         wutils.connect_to_wifi_network(self.dut_client, softap)
@@ -553,21 +564,25 @@
             AP2_network["bssid"] = self.bssid_map[1]["5g"][AP2_network["SSID"]]
         wutils.set_attns(self.attenuators, "AP1_on_AP2_off", self.roaming_attn)
         mac_before_roam = self.connect_to_network_and_verify_mac_randomization(
-                AP1_network)
+            AP1_network)
         wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
-                "AP1_off_AP2_on", AP2_network, self.roaming_attn)
+                                            "AP1_off_AP2_on", AP2_network,
+                                            self.roaming_attn)
         mac_after_roam = self.get_randomized_mac(AP2_network)
         if mac_after_roam != mac_before_roam:
-            raise signals.TestFailure("Randomized MAC address changed after "
-                   "roaming from AP1 to AP2.\nMAC before roam = %s\nMAC after "
-                   "roam = %s" %(mac_before_roam, mac_after_roam))
+            raise signals.TestFailure(
+                "Randomized MAC address changed after "
+                "roaming from AP1 to AP2.\nMAC before roam = %s\nMAC after "
+                "roam = %s" % (mac_before_roam, mac_after_roam))
         wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
-                "AP1_on_AP2_off", AP1_network, self.roaming_attn)
+                                            "AP1_on_AP2_off", AP1_network,
+                                            self.roaming_attn)
         mac_after_roam = self.get_randomized_mac(AP1_network)
         if mac_after_roam != mac_before_roam:
-            raise signals.TestFailure("Randomized MAC address changed after "
-                   "roaming from AP1 to AP2.\nMAC before roam = %s\nMAC after "
-                   "roam = %s" %(mac_before_roam, mac_after_roam))
+            raise signals.TestFailure(
+                "Randomized MAC address changed after "
+                "roaming from AP1 to AP2.\nMAC before roam = %s\nMAC after "
+                "roam = %s" % (mac_before_roam, mac_after_roam))
 
     @test_tracker_info(uuid="17b12f1a-7c62-4188-b5a5-52d7a0bb7849")
     def test_check_mac_sta_with_link_probe(self):
@@ -582,11 +597,12 @@
             6. Read each packet summary and make sure Factory MAC is not used.
 
         """
-        self.pcap_procs = wutils.start_pcap(
-            self.packet_capture, 'dual', self.test_name)
+        self.pcap_procs = wutils.start_pcap(self.packet_capture, 'dual',
+                                            self.test_name)
         time.sleep(SHORT_TIMEOUT)
         network = self.wpapsk_5g
-        rand_mac = self.connect_to_network_and_verify_mac_randomization(network)
+        rand_mac = self.connect_to_network_and_verify_mac_randomization(
+            network)
         pcap_fname_bflink = '%s_%s.pcap' % \
             (self.pcap_procs[hostapd_constants.BAND_5G][1],
              hostapd_constants.BAND_5G.upper())
@@ -595,8 +611,8 @@
         packets_bflink = rdpcap(pcap_fname_bflink)
         self.verify_mac_not_found_in_pcap(self.sta_factory_mac, packets_bflink)
         self.verify_mac_is_found_in_pcap(rand_mac, packets_bflink)
-        self.pcap_procs = wutils.start_pcap(
-            self.packet_capture, 'dual', self.test_name)
+        self.pcap_procs = wutils.start_pcap(self.packet_capture, 'dual',
+                                            self.test_name)
         time.sleep(SHORT_TIMEOUT)
         wutils.send_link_probes(self.dut, 3, 3)
         pcap_fname = '%s_%s.pcap' % \
@@ -620,8 +636,8 @@
           5. Read each packet summary and make sure Factory MAC is not used.
 
         """
-        self.pcap_procs = wutils.start_pcap(
-            self.packet_capture, 'dual', self.test_name)
+        self.pcap_procs = wutils.start_pcap(self.packet_capture, 'dual',
+                                            self.test_name)
         wutils.start_wifi_connection_scan(self.dut)
         time.sleep(SHORT_TIMEOUT)
         wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
@@ -639,7 +655,7 @@
             1. Add a network suggestion and verify device connects to it.
             2. Verify the device uses randomized MAC address for this network.
         """
-        network_suggestion = self.reference_networks[0]["5g"]
+        network_suggestion = self.reference_networks[0]["2g"]
         self._add_suggestion_and_verify_mac_randomization(network_suggestion)
 
     @test_tracker_info(uuid="144ad0b4-b79d-4b1d-a8a9-3c612a76c32c")
diff --git a/acts_tests/tests/google/wifi/WifiManagerTest.py b/acts_tests/tests/google/wifi/WifiManagerTest.py
index d2a07dc..f386438 100644
--- a/acts_tests/tests/google/wifi/WifiManagerTest.py
+++ b/acts_tests/tests/google/wifi/WifiManagerTest.py
@@ -113,6 +113,10 @@
             acts.utils.force_airplane_mode(self.dut, False),
             "Can not turn airplane mode off: %s" % self.dut.serial)
 
+        if self.dut.model in self.user_params["google_pixel_watch_models"]:
+            if wutils.get_wear_wifimediator_disable_status(self.dut):
+                wutils.disable_wear_wifimediator(self.dut, False)
+
     def teardown_class(self):
         if "AccessPoint" in self.user_params:
             del self.user_params["reference_networks"]
@@ -634,6 +638,10 @@
     def test_scan_with_wifi_off_and_location_scan_off(self):
         """Turn off wifi and location scan"""
         self.turn_location_on_and_scan_toggle_off()
+
+        if self.dut.model in self.user_params["google_pixel_watch_models"]:
+            wutils.disable_wear_wifimediator(self.dut, True)
+
         wutils.wifi_toggle_state(self.dut, False)
 
         """Test wifi connection scan should fail."""
diff --git a/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py b/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py
index 0d13a0b..ba07283 100644
--- a/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py
+++ b/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py
@@ -14,7 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import logging
 import time
 
 import acts.signals as signals
@@ -44,6 +43,7 @@
     """These tests verify the behavior of the Android Wi-Fi Network Selector
     feature.
     """
+
     def __init__(self, configs):
         super().__init__(configs)
         self.enable_packet_log = True
@@ -58,8 +58,7 @@
         self.ap2_2g_attn = 2
         self.ap2_5g_attn = 3
         if "AccessPoint" in self.user_params:
-            self.legacy_configure_ap_and_start(mirror_ap=False,
-                                               ap_count=2)
+            self.legacy_configure_ap_and_start(mirror_ap=False, ap_count=2)
         elif "OpenWrtAP" in self.user_params:
             self.configure_openwrt_ap_and_start(open_network=True,
                                                 wpa_network=True,
@@ -164,8 +163,10 @@
             3. Verify the DUT is connected to the 5G BSSID.
         """
         # add a saved network with both 2G and 5G BSSIDs to DUT
-        networks = [self.reference_networks[AP_1]['2g'],
-                    self.reference_networks[AP_1]['5g']]
+        networks = [
+            self.reference_networks[AP_1]['2g'],
+            self.reference_networks[AP_1]['5g']
+        ]
         self.add_networks(self.dut, networks)
 
         # Move DUT in range
@@ -188,8 +189,10 @@
             3. Verify the DUT is connected to the SSID with stronger RSSI.
         """
         # add a 2G and a 5G saved network to DUT
-        networks = [self.reference_networks[AP_1]['2g'],
-                    self.reference_networks[AP_2]['2g']]
+        networks = [
+            self.reference_networks[AP_1]['2g'],
+            self.reference_networks[AP_2]['2g']
+        ]
         self.add_networks(self.dut, networks)
 
         # move the DUT in range
@@ -212,8 +215,9 @@
             3. Verify the DUT is connected to the secure network that uses WPA2.
         """
         # add a open network and a secure saved network to DUT
-        networks = [self.open_network[AP_1]['5g'],
-                    self.reference_networks[AP_1]['5g']]
+        networks = [
+            self.open_network[AP_1]['5g'], self.reference_networks[AP_1]['5g']
+        ]
         self.add_networks(self.dut, networks)
 
         # Move DUT in range
@@ -270,23 +274,23 @@
         """
         #add two saved networks to DUT
         networks = [
-            self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][
-                '2g']
+            self.reference_networks[AP_1]['2g'],
+            self.reference_networks[AP_2]['2g']
         ]
         self.add_networks(self.dut, networks)
         #make AP_1 2G in range
         self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
         #verify
-        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
-            '2g']['bssid'])
+        self.connect_and_verify_connected_bssid(
+            self.reference_networks[AP_1]['2g']['bssid'])
         #make both AP_1 and AP_2 5G in range with similar RSSI
         self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
         self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
         #ensure the time gap between two network selections
         time.sleep(NETWORK_SELECTION_TIME_GAP)
         #verify
-        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
-            '5g']['bssid'])
+        self.connect_and_verify_connected_bssid(
+            self.reference_networks[AP_1]['5g']['bssid'])
 
     @test_tracker_info(uuid="c1243cf4-d96e-427e-869e-3d640bee3f28")
     def network_selector_2g_to_5g_different_ssid(self):
@@ -300,8 +304,10 @@
             4. Verify the DUT switches to SSID_B's 5G.
         """
         # add two saved networks to DUT
-        networks = [self.reference_networks[AP_1]['2g'],
-                    self.reference_networks[AP_2]['2g']]
+        networks = [
+            self.reference_networks[AP_1]['2g'],
+            self.reference_networks[AP_2]['2g']
+        ]
         self.add_networks(self.dut, networks)
 
         # make both AP_1 2G and AP_2 5G in range, and AP_1 2G
@@ -309,16 +315,16 @@
         self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
         self.attenuators[AP_2_5G_ATTENUATOR].set_atten(20)
         #verify
-        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
-            '2g']['bssid'])
+        self.connect_and_verify_connected_bssid(
+            self.reference_networks[AP_1]['2g']['bssid'])
         #bump up AP_2 5G RSSI and reduce AP_1 2G RSSI
         self.attenuators[AP_1_2G_ATTENUATOR].set_atten(40)
         self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
         #ensure the time gap between two network selections
         time.sleep(NETWORK_SELECTION_TIME_GAP)
         #verify
-        self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][
-            '5g']['bssid'])
+        self.connect_and_verify_connected_bssid(
+            self.reference_networks[AP_2]['5g']['bssid'])
 
     @test_tracker_info(uuid="10da95df-83ed-4447-89f8-735b08dbe2eb")
     def network_selector_5g_to_2g_same_ssid(self):
@@ -337,16 +343,16 @@
         self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
         self.attenuators[AP_1_2G_ATTENUATOR].set_atten(50)
         #verify
-        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
-            '5g']['bssid'])
+        self.connect_and_verify_connected_bssid(
+            self.reference_networks[AP_1]['5g']['bssid'])
         #bump up AP_1 2G RSSI and reduce AP_1 5G RSSI
         self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
         self.attenuators[AP_1_5G_ATTENUATOR].set_atten(30)
         #ensure the time gap between two network selections
         time.sleep(NETWORK_SELECTION_TIME_GAP)
         #verify
-        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
-            '2g']['bssid'])
+        self.connect_and_verify_connected_bssid(
+            self.reference_networks[AP_1]['2g']['bssid'])
 
     @test_tracker_info(uuid="ead78ae0-27ab-4bb8-ae77-0b9fe588436a")
     def test_network_selector_stay_on_sufficient_network(self):
@@ -358,8 +364,10 @@
             4. Verify the DUT stays on X.
         """
         # add two saved networks to DUT
-        networks = [self.reference_networks[AP_1]['5g'],
-                    self.reference_networks[AP_2]['5g']]
+        networks = [
+            self.reference_networks[AP_1]['5g'],
+            self.reference_networks[AP_2]['5g']
+        ]
         self.add_networks(self.dut, networks)
 
         # make both AP_1 5G and AP_2 5G in range, and AP_1 5G
@@ -397,8 +405,8 @@
         time.sleep(ATTN_SLEEP)
 
         # connect to AP_1 via user selection and add, save AP_2
-        wutils.connect_to_wifi_network(
-            self.dut, self.reference_networks[AP_1]['5g'])
+        wutils.connect_to_wifi_network(self.dut,
+                                       self.reference_networks[AP_1]['5g'])
         networks = [self.reference_networks[AP_2]['5g']]
         self.add_networks(self.dut, networks)
 
@@ -421,8 +429,10 @@
             5. Verify the DUT reselect and connect to Y.
         """
         # add two networks to DUT
-        networks = [self.reference_networks[AP_1]['5g'],
-                    self.reference_networks[AP_2]['5g']]
+        networks = [
+            self.reference_networks[AP_1]['5g'],
+            self.reference_networks[AP_2]['5g']
+        ]
         self.add_networks(self.dut, networks)
 
         # make both AP_1 5G and AP_2 5G in range. AP_1 5G has stronger
@@ -438,8 +448,8 @@
         self.connect_and_verify_connected_bssid(network)
 
         # forget AP_1
-        wutils.wifi_forget_network(
-            self.dut, self.reference_networks[AP_1]['5g']['SSID'])
+        wutils.wifi_forget_network(self.dut,
+                                   self.reference_networks[AP_1]['5g']['SSID'])
 
         # verify DUT connected to AP2
         network = self.reference_networks[AP_2]['5g'].copy()
diff --git a/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py b/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py
index f782760..f3b9c66 100644
--- a/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py
+++ b/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py
@@ -15,7 +15,6 @@
 #   limitations under the License.
 
 import itertools
-import pprint
 import queue
 import time
 
@@ -45,7 +44,6 @@
 ClearCapabilities = "ClearCapabilities"
 TransportType = "TransportType"
 
-
 # Default timeout used for reboot, toggle WiFi and Airplane mode,
 # for the system to settle down after the operation.
 DEFAULT_TIMEOUT = 10
@@ -60,6 +58,7 @@
     * Several Wi-Fi networks visible to the device, including an open Wi-Fi
       network.
     """
+
     def __init__(self, configs):
         super().__init__(configs)
         self.enable_packet_log = True
@@ -69,36 +68,44 @@
 
         self.dut = self.android_devices[0]
         opt_param = [
-            "open_network", "reference_networks", "hidden_networks", "radius_conf_2g",
-            "radius_conf_5g", "ca_cert", "eap_identity", "eap_password", "passpoint_networks",
-            "domain_suffix_match", "wifi6_models"]
-        self.unpack_userparams(opt_param_names=opt_param,)
+            "open_network", "reference_networks", "hidden_networks",
+            "radius_conf_2g", "radius_conf_5g", "ca_cert", "eap_identity",
+            "eap_password", "passpoint_networks", "domain_suffix_match",
+            "wifi6_models"
+        ]
+        self.unpack_userparams(opt_param_names=opt_param, )
 
         if "AccessPoint" in self.user_params:
             self.legacy_configure_ap_and_start(
-                wpa_network=True, ent_network=True,
+                wpa_network=True,
+                ent_network=True,
                 radius_conf_2g=self.radius_conf_2g,
-                radius_conf_5g=self.radius_conf_5g,)
+                radius_conf_5g=self.radius_conf_5g,
+            )
         elif "OpenWrtAP" in self.user_params:
-            self.configure_openwrt_ap_and_start(open_network=True,
-                                                wpa_network=True,)
+            self.configure_openwrt_ap_and_start(
+                open_network=True,
+                wpa_network=True,
+            )
         if hasattr(self, "reference_networks") and \
             isinstance(self.reference_networks, list):
-              self.wpa_psk_2g = self.reference_networks[0]["2g"]
-              self.wpa_psk_5g = self.reference_networks[0]["5g"]
-        if hasattr(self, "open_network") and isinstance(self.open_network,list):
+            self.wpa_psk_2g = self.reference_networks[0]["2g"]
+            self.wpa_psk_5g = self.reference_networks[0]["5g"]
+        if hasattr(self, "open_network") and isinstance(
+                self.open_network, list):
             self.open_2g = self.open_network[0]["2g"]
             self.open_5g = self.open_network[0]["5g"]
         if hasattr(self, "hidden_networks") and \
             isinstance(self.hidden_networks, list):
-              self.hidden_network = self.hidden_networks[0]
+            self.hidden_network = self.hidden_networks[0]
         if hasattr(self, "passpoint_networks"):
             self.passpoint_network = self.passpoint_networks[BOINGO]
             self.passpoint_network[WifiEnums.SSID_KEY] = \
                 self.passpoint_networks[BOINGO][WifiEnums.SSID_KEY][0]
         self.dut.droid.wifiRemoveNetworkSuggestions([])
         self.dut.adb.shell(
-            "pm disable com.google.android.apps.carrier.carrierwifi", ignore_status=True)
+            "pm disable com.google.android.apps.carrier.carrierwifi",
+            ignore_status=True)
 
     def setup_test(self):
         super().setup_test()
@@ -108,14 +115,16 @@
         self.clear_user_disabled_networks()
         wutils.wifi_toggle_state(self.dut, True)
         self.dut.ed.clear_all_events()
-        self.clear_carrier_approved(str(self.dut.droid.telephonyGetSimCarrierId()))
+        self.clear_carrier_approved(
+            str(self.dut.droid.telephonyGetSimCarrierId()))
         if "_ent_" in self.test_name:
             if "OpenWrtAP" in self.user_params:
                 self.access_points[0].close()
                 self.configure_openwrt_ap_and_start(
                     ent_network=True,
                     radius_conf_2g=self.radius_conf_2g,
-                    radius_conf_5g=self.radius_conf_5g,)
+                    radius_conf_5g=self.radius_conf_5g,
+                )
             self.ent_network_2g = self.ent_networks[0]["2g"]
             self.ent_network_5g = self.ent_networks[0]["5g"]
 
@@ -128,7 +137,8 @@
         wutils.reset_wifi(self.dut)
         wutils.wifi_toggle_state(self.dut, False)
         self.dut.ed.clear_all_events()
-        self.clear_carrier_approved(str(self.dut.droid.telephonyGetSimCarrierId()))
+        self.clear_carrier_approved(
+            str(self.dut.droid.telephonyGetSimCarrierId()))
 
     def teardown_class(self):
         self.dut.adb.shell(
@@ -138,25 +148,27 @@
             del self.user_params["open_network"]
 
     """Helper Functions"""
+
     def set_approved(self, approved):
-        self.dut.log.debug("Setting suggestions from sl4a app "
-                           + "approved" if approved else "not approved")
-        self.dut.adb.shell("cmd wifi network-suggestions-set-user-approved"
-                           + " " + SL4A_APK_NAME
-                           + " " + ("yes" if approved else "no"))
+        self.dut.log.debug("Setting suggestions from sl4a app " +
+                           "approved" if approved else "not approved")
+        self.dut.adb.shell("cmd wifi network-suggestions-set-user-approved" +
+                           " " + SL4A_APK_NAME + " " +
+                           ("yes" if approved else "no"))
 
     def is_approved(self):
         is_approved_str = self.dut.adb.shell(
-            "cmd wifi network-suggestions-has-user-approved"
-            + " " + SL4A_APK_NAME)
+            "cmd wifi network-suggestions-has-user-approved" + " " +
+            SL4A_APK_NAME)
         return True if (is_approved_str == "yes") else False
 
     def set_carrier_approved(self, carrier_id, approved):
-        self.dut.log.debug(("Setting IMSI protection exemption for carrier: " + carrier_id
-                                + "approved" if approved else "not approved"))
-        self.dut.adb.shell("cmd wifi imsi-protection-exemption-set-user-approved-for-carrier"
-                           + " " + carrier_id
-                           + " " + ("yes" if approved else "no"))
+        self.dut.log.debug(
+            ("Setting IMSI protection exemption for carrier: " + carrier_id +
+             "approved" if approved else "not approved"))
+        self.dut.adb.shell(
+            "cmd wifi imsi-protection-exemption-set-user-approved-for-carrier"
+            + " " + carrier_id + " " + ("yes" if approved else "no"))
 
     def is_carrier_approved(self, carrier_id):
         is_approved_str = self.dut.adb.shell(
@@ -171,12 +183,11 @@
 
     def clear_user_disabled_networks(self):
         self.dut.log.debug("Clearing user disabled networks")
-        self.dut.adb.shell(
-            "cmd wifi clear-user-disabled-networks")
+        self.dut.adb.shell("cmd wifi clear-user-disabled-networks")
 
-    def add_suggestions_and_ensure_connection(self, network_suggestions,
-                                              expected_ssid,
-                                              expect_post_connection_broadcast):
+    def add_suggestions_and_ensure_connection(
+            self, network_suggestions, expected_ssid,
+            expect_post_connection_broadcast):
         if expect_post_connection_broadcast is not None:
             self.dut.droid.wifiStartTrackingNetworkSuggestionStateChange()
 
@@ -206,15 +217,13 @@
                     "Did not receive post connection broadcast")
         else:
             if not expect_post_connection_broadcast:
-                raise signals.TestFailure(
-                    "Received post connection broadcast")
+                raise signals.TestFailure("Received post connection broadcast")
         finally:
             self.dut.droid.wifiStopTrackingNetworkSuggestionStateChange()
         self.dut.ed.clear_all_events()
 
-    def remove_suggestions_disconnect_and_ensure_no_connection_back(self,
-                                                                    network_suggestions,
-                                                                    expected_ssid):
+    def remove_suggestions_disconnect_and_ensure_no_connection_back(
+            self, network_suggestions, expected_ssid):
         # Remove suggestion trigger disconnect and wait for the disconnect.
         self.dut.log.info("Removing network suggestions")
         asserts.assert_true(
@@ -225,12 +234,13 @@
 
         # Now ensure that we didn't connect back.
         asserts.assert_false(
-            wutils.wait_for_connect(self.dut, expected_ssid, assert_on_fail=False),
+            wutils.wait_for_connect(self.dut,
+                                    expected_ssid,
+                                    assert_on_fail=False),
             "Device should not connect back")
 
-    def _test_connect_to_wifi_network_reboot_config_store(self,
-                                                          network_suggestions,
-                                                          wifi_network):
+    def _test_connect_to_wifi_network_reboot_config_store(
+            self, network_suggestions, wifi_network):
         """ Test network suggestion with reboot config store
 
         Args:
@@ -240,16 +250,16 @@
 
         self.add_suggestions_and_ensure_connection(
             network_suggestions, wifi_network[WifiEnums.SSID_KEY], None)
-        wutils.verify_11ax_wifi_connection(
-            self.dut, self.wifi6_models, "wifi6_ap" in self.user_params)
+        wutils.verify_11ax_wifi_connection(self.dut, self.wifi6_models,
+                                           "wifi6_ap" in self.user_params)
 
         # Reboot and wait for connection back to the same suggestion.
         self.dut.reboot()
         time.sleep(DEFAULT_TIMEOUT)
 
         wutils.wait_for_connect(self.dut, wifi_network[WifiEnums.SSID_KEY])
-        wutils.verify_11ax_wifi_connection(
-            self.dut, self.wifi6_models, "wifi6_ap" in self.user_params)
+        wutils.verify_11ax_wifi_connection(self.dut, self.wifi6_models,
+                                           "wifi6_ap" in self.user_params)
 
         self.remove_suggestions_disconnect_and_ensure_no_connection_back(
             network_suggestions, wifi_network[WifiEnums.SSID_KEY])
@@ -271,8 +281,7 @@
         4. Remove the suggestions and ensure the device does not connect back.
         """
         self.add_suggestions_and_ensure_connection(
-            [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY],
-            False)
+            [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY], False)
 
         self.remove_suggestions_disconnect_and_ensure_no_connection_back(
             [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY])
@@ -310,8 +319,7 @@
 
         # Add valid suggestions & ensure we restart PNO and connect to it.
         self.add_suggestions_and_ensure_connection(
-            [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY],
-            False)
+            [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY], False)
 
         self.remove_suggestions_disconnect_and_ensure_no_connection_back(
             [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY])
@@ -334,8 +342,7 @@
         8. Remove the suggestions and ensure the device does not connect back.
         """
         self.add_suggestions_and_ensure_connection(
-            [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY],
-            False)
+            [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY], False)
 
         mod_suggestion = self.wpa_psk_2g
 
@@ -365,7 +372,6 @@
         self.remove_suggestions_disconnect_and_ensure_no_connection_back(
             [mod_suggestion], mod_suggestion[WifiEnums.SSID_KEY])
 
-
     @test_tracker_info(uuid="f54bc250-d9e9-4f00-8b5b-b866e8550b43")
     def test_connect_to_highest_priority(self):
         """
@@ -391,16 +397,15 @@
         network_suggestion_5g[WifiEnums.PRIORITY] = 2
         self.add_suggestions_and_ensure_connection(
             [network_suggestion_2g, network_suggestion_5g],
-            self.wpa_psk_2g[WifiEnums.SSID_KEY],
-            None)
+            self.wpa_psk_2g[WifiEnums.SSID_KEY], None)
 
         # In-place modify Reverse the priority, should be no disconnect
         network_suggestion_2g[WifiEnums.PRIORITY] = 2
         network_suggestion_5g[WifiEnums.PRIORITY] = 5
         self.dut.log.info("Modifying network suggestions")
         asserts.assert_true(
-            self.dut.droid.wifiAddNetworkSuggestions([network_suggestion_2g,
-                                                      network_suggestion_5g]),
+            self.dut.droid.wifiAddNetworkSuggestions(
+                [network_suggestion_2g, network_suggestion_5g]),
             "Failed to add suggestions")
         wutils.ensure_no_disconnect(self.dut)
 
@@ -420,8 +425,7 @@
         network_suggestion_5g[WifiEnums.PRIORITY] = 2
         self.add_suggestions_and_ensure_connection(
             [network_suggestion_2g, network_suggestion_5g],
-            self.wpa_psk_2g[WifiEnums.SSID_KEY],
-            None)
+            self.wpa_psk_2g[WifiEnums.SSID_KEY], None)
 
     @test_tracker_info(uuid="b1d27eea-23c8-4c4f-b944-ef118e4cc35f")
     def test_connect_to_wpa_psk_2g_with_post_connection_broadcast(self):
@@ -438,8 +442,7 @@
         network_suggestion = self.wpa_psk_2g
         network_suggestion[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True
         self.add_suggestions_and_ensure_connection(
-            [network_suggestion], self.wpa_psk_2g[WifiEnums.SSID_KEY],
-            True)
+            [network_suggestion], self.wpa_psk_2g[WifiEnums.SSID_KEY], True)
         self.remove_suggestions_disconnect_and_ensure_no_connection_back(
             [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY])
 
@@ -541,9 +544,8 @@
         self.set_approved(False)
 
         # Ensure the app is not approved.
-        asserts.assert_false(
-            self.is_approved(),
-            "Suggestions should be disabled")
+        asserts.assert_false(self.is_approved(),
+                             "Suggestions should be disabled")
 
         # Start a new scan to trigger auto-join.
         wutils.start_wifi_connection_scan_and_ensure_network_found(
@@ -551,8 +553,9 @@
 
         # Ensure we don't connect to the network.
         asserts.assert_false(
-            wutils.wait_for_connect(
-                self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY], assert_on_fail=False),
+            wutils.wait_for_connect(self.dut,
+                                    self.wpa_psk_5g[WifiEnums.SSID_KEY],
+                                    assert_on_fail=False),
             "Should not connect to network suggestions from unapproved app")
 
         self.dut.log.info("Enabling suggestions from test")
@@ -560,9 +563,8 @@
         self.set_approved(True)
 
         # Ensure the app is approved.
-        asserts.assert_true(
-            self.is_approved(),
-            "Suggestions should be enabled")
+        asserts.assert_true(self.is_approved(),
+                            "Suggestions should be enabled")
 
         # Start a new scan to trigger auto-join.
         wutils.start_wifi_connection_scan_and_ensure_network_found(
@@ -588,8 +590,7 @@
         network_suggestion = self.wpa_psk_2g
         network_suggestion[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True
         self.add_suggestions_and_ensure_connection(
-            [network_suggestion], self.wpa_psk_2g[WifiEnums.SSID_KEY],
-            True)
+            [network_suggestion], self.wpa_psk_2g[WifiEnums.SSID_KEY], True)
 
         # Simulate user disconnect the network.
         self.dut.droid.wifiUserDisconnectNetwork(
@@ -619,11 +620,13 @@
            (isAppInteractionRequired = False).
         4. Remove the suggestions and ensure the device does not connect back.
         """
-        asserts.skip_if(not hasattr(self, "hidden_networks"), "No hidden networks, skip this test")
+        asserts.skip_if(not hasattr(self, "hidden_networks"),
+                        "No hidden networks, skip this test")
 
         network_suggestion = self.hidden_network
         self.add_suggestions_and_ensure_connection(
-            [network_suggestion], network_suggestion[WifiEnums.SSID_KEY], False)
+            [network_suggestion], network_suggestion[WifiEnums.SSID_KEY],
+            False)
         self.remove_suggestions_disconnect_and_ensure_no_connection_back(
             [network_suggestion], network_suggestion[WifiEnums.SSID_KEY])
 
@@ -644,8 +647,8 @@
         passpoint_config[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True
         if "carrierId" in passpoint_config:
             self.set_carrier_approved(passpoint_config["carrierId"], True)
-        self.add_suggestions_and_ensure_connection([passpoint_config],
-                                                   passpoint_config[WifiEnums.SSID_KEY], True)
+        self.add_suggestions_and_ensure_connection(
+            [passpoint_config], passpoint_config[WifiEnums.SSID_KEY], True)
         self.remove_suggestions_disconnect_and_ensure_no_connection_back(
             [passpoint_config], passpoint_config[WifiEnums.SSID_KEY])
         if "carrierId" in passpoint_config:
@@ -672,8 +675,8 @@
         passpoint_config = self.passpoint_network
         if "carrierId" in passpoint_config:
             self.set_carrier_approved(passpoint_config["carrierId"], True)
-        self._test_connect_to_wifi_network_reboot_config_store([passpoint_config],
-                                                               passpoint_config)
+        self._test_connect_to_wifi_network_reboot_config_store(
+            [passpoint_config], passpoint_config)
         if "carrierId" in passpoint_config:
             self.clear_carrier_approved(passpoint_config["carrierId"])
 
@@ -704,9 +707,8 @@
         self.set_approved(False)
 
         # Ensure the app is not approved.
-        asserts.assert_false(
-            self.is_approved(),
-            "Suggestions should be disabled")
+        asserts.assert_false(self.is_approved(),
+                             "Suggestions should be disabled")
 
         # Start a new scan to trigger auto-join.
         wutils.start_wifi_connection_scan_and_ensure_network_found(
@@ -714,8 +716,9 @@
 
         # Ensure we don't connect to the network.
         asserts.assert_false(
-            wutils.wait_for_connect(
-                self.dut, passpoint_config[WifiEnums.SSID_KEY], assert_on_fail=False),
+            wutils.wait_for_connect(self.dut,
+                                    passpoint_config[WifiEnums.SSID_KEY],
+                                    assert_on_fail=False),
             "Should not connect to network suggestions from unapproved app")
 
         self.dut.log.info("Enabling suggestions from test")
@@ -723,9 +726,8 @@
         self.set_approved(True)
 
         # Ensure the app is approved.
-        asserts.assert_true(
-            self.is_approved(),
-            "Suggestions should be enabled")
+        asserts.assert_true(self.is_approved(),
+                            "Suggestions should be enabled")
 
         # Start a new scan to trigger auto-join.
         wutils.start_wifi_connection_scan_and_ensure_network_found(
@@ -736,7 +738,8 @@
             self.clear_carrier_approved(passpoint_config["carrierId"])
 
     @test_tracker_info(uuid="cf624cda-4d25-42f1-80eb-6c717fb08338")
-    def test_fail_to_connect_to_passpoint_network_when_imsi_protection_exemption_not_approved(self):
+    def test_fail_to_connect_to_passpoint_network_when_imsi_protection_exemption_not_approved(
+            self):
         """
         Adds a passpoint network suggestion using SIM credential without IMSI privacy protection.
         Before user approves the exemption, ensure that the device does noconnect to it until we
@@ -752,8 +755,8 @@
         asserts.skip_if(not hasattr(self, "passpoint_networks"),
                         "No passpoint networks, skip this test")
         passpoint_config = self.passpoint_networks[ATT]
-        passpoint_config[WifiEnums.SSID_KEY] = self.passpoint_networks[
-                ATT][WifiEnums.SSID_KEY][0]
+        passpoint_config[WifiEnums.SSID_KEY] = self.passpoint_networks[ATT][
+            WifiEnums.SSID_KEY][0]
         asserts.skip_if("carrierId" not in passpoint_config,
                         "Not a SIM based passpoint network, skip this test")
 
@@ -773,8 +776,9 @@
 
         # Ensure we don't connect to the network.
         asserts.assert_false(
-            wutils.wait_for_connect(
-                self.dut, passpoint_config[WifiEnums.SSID_KEY], assert_on_fail=False),
+            wutils.wait_for_connect(self.dut,
+                                    passpoint_config[WifiEnums.SSID_KEY],
+                                    assert_on_fail=False),
             "Should not connect to network suggestions from unapproved app")
 
         self.dut.log.info("Enabling suggestions from test")
@@ -817,8 +821,10 @@
         self.set_approved(True)
         wutils.start_wifi_connection_scan_and_return_status(self.dut)
         asserts.assert_false(
-            wutils.wait_for_connect(self.dut, network_suggestion[WifiEnums.SSID_KEY],
-                                    assert_on_fail=False), "Device should not connect.")
+            wutils.wait_for_connect(self.dut,
+                                    network_suggestion[WifiEnums.SSID_KEY],
+                                    assert_on_fail=False),
+            "Device should not connect.")
 
     @test_tracker_info(uuid="ff4e451f-a380-4ff5-a5c2-dd9b1633d5e5")
     def test_user_override_auto_join_on_network_suggestion(self):
@@ -834,8 +840,9 @@
         4. Ensure device doesn't connect to his network
         """
         network_suggestion = self.wpa_psk_5g
-        self.add_suggestions_and_ensure_connection([network_suggestion],
-                                                   network_suggestion[WifiEnums.SSID_KEY], False)
+        self.add_suggestions_and_ensure_connection(
+            [network_suggestion], network_suggestion[WifiEnums.SSID_KEY],
+            False)
         wifi_info = self.dut.droid.wifiGetConnectionInfo()
         self.dut.log.info(wifi_info)
         network_id = wifi_info[WifiEnums.NETID_KEY]
@@ -845,8 +852,10 @@
         wutils.wifi_toggle_state(self.dut, False)
         wutils.wifi_toggle_state(self.dut, True)
         asserts.assert_false(
-            wutils.wait_for_connect(self.dut, network_suggestion[WifiEnums.SSID_KEY],
-                                    assert_on_fail=False), "Device should not connect.")
+            wutils.wait_for_connect(self.dut,
+                                    network_suggestion[WifiEnums.SSID_KEY],
+                                    assert_on_fail=False),
+            "Device should not connect.")
 
     @test_tracker_info(uuid="32201b1c-76a0-46dc-9983-2cd24312a783")
     def test_untrusted_suggestion_without_untrusted_request(self):
@@ -869,9 +878,11 @@
 
         # Ensure we don't connect to the network.
         asserts.assert_false(
-            wutils.wait_for_connect(
-                self.dut, network_suggestion[WifiEnums.SSID_KEY], assert_on_fail=False),
-            "Should not connect to untrusted network suggestions with no request")
+            wutils.wait_for_connect(self.dut,
+                                    network_suggestion[WifiEnums.SSID_KEY],
+                                    assert_on_fail=False),
+            "Should not connect to untrusted network suggestions with no request"
+        )
         network_request = {ClearCapabilities: True, TransportType: 1}
         req_key = self.dut.droid.connectivityRequestNetwork(network_request)
 
@@ -879,8 +890,8 @@
         wutils.start_wifi_connection_scan_and_ensure_network_found(
             self.dut, network_suggestion[WifiEnums.SSID_KEY])
 
-        wutils.wait_for_connect(
-            self.dut, network_suggestion[WifiEnums.SSID_KEY], assert_on_fail=False)
+        wutils.wait_for_connect(self.dut,
+                                network_suggestion[WifiEnums.SSID_KEY],
+                                assert_on_fail=False)
 
         self.dut.droid.connectivityUnregisterNetworkCallback(req_key)
-
diff --git a/acts_tests/tests/google/wifi/WifiPasspointLanguageTest.py b/acts_tests/tests/google/wifi/WifiPasspointLanguageTest.py
index c791de1..523a810 100644
--- a/acts_tests/tests/google/wifi/WifiPasspointLanguageTest.py
+++ b/acts_tests/tests/google/wifi/WifiPasspointLanguageTest.py
@@ -15,8 +15,6 @@
 #   limitations under the License.
 
 import itertools
-import pprint
-import queue
 import time
 
 from acts_contrib.test_utils.net import ui_utils as uutils
@@ -45,6 +43,7 @@
 PASSWORD_TEXT = "Password"
 PASSPOINT_BUTTON = "Get Passpoint"
 
+
 class WifiPasspointLanguageTest(WifiBaseTest):
     """Tests for APIs in Android's WifiManager class.
 
@@ -56,20 +55,20 @@
     BOINGO_UI_TEXT = {
         'CHT': "線上註冊",
         'FRA': "Inscription en ligne",
-        'US' : "Online Sign Up",
+        'US': "Online Sign Up",
         'SPA': "Registro online",
-        'ARA' : "الاشتراك على الإنترنت"
+        'ARA': "الاشتراك على الإنترنت"
     }
 
     def setup_class(self):
         super().setup_class()
         self.dut = self.android_devices[0]
         wutils.wifi_test_device_init(self.dut)
-        req_params = ["passpoint_networks",
-                      "boingo_username",
-                      "boingo_password",
-                      "osu_configs"]
-        self.unpack_userparams(req_param_names=req_params,)
+        req_params = [
+            "passpoint_networks", "boingo_username", "boingo_password",
+            "osu_configs"
+        ]
+        self.unpack_userparams(req_param_names=req_params, )
         asserts.assert_true(
             len(self.passpoint_networks) > 0,
             "Need at least one Passpoint network.")
@@ -93,7 +92,6 @@
         wutils.reset_wifi(self.dut)
         self.language_change('US')
 
-
     """Helper Functions"""
 
     def install_passpoint_profile(self, passpoint_config):
@@ -103,9 +101,10 @@
             passpoint_config: A JSON dict of the Passpoint configuration.
 
         """
-        asserts.assert_true(WifiEnums.SSID_KEY in passpoint_config,
-                "Key '%s' must be present in network definition." %
-                WifiEnums.SSID_KEY)
+        asserts.assert_true(
+            WifiEnums.SSID_KEY in passpoint_config,
+            "Key '%s' must be present in network definition." %
+            WifiEnums.SSID_KEY)
         # Install the Passpoint profile.
         self.dut.droid.addUpdatePasspointConfig(passpoint_config)
 
@@ -129,8 +128,8 @@
         self.log.info("Network Info: %s" % network_info)
         if not network_info or not network_info[WifiEnums.SSID_KEY] or \
             network_info[WifiEnums.SSID_KEY] not in passpoint_network:
-              raise signals.TestFailure(
-                  "Device did not connect to passpoint network.")
+            raise signals.TestFailure(
+                "Device did not connect to passpoint network.")
 
     def get_configured_passpoint_and_delete(self):
         """Get configured Passpoint network and delete using its FQDN."""
@@ -139,8 +138,9 @@
             raise signals.TestFailure("Failed to fetch the list of configured"
                                       "passpoint networks.")
         if not wutils.delete_passpoint(self.dut, passpoint_config[0]):
-            raise signals.TestFailure("Failed to delete Passpoint configuration"
-                                      " with FQDN = %s" % passpoint_config[0])
+            raise signals.TestFailure(
+                "Failed to delete Passpoint configuration"
+                " with FQDN = %s" % passpoint_config[0])
 
     def language_change(self, lang):
         """Run UI automator for boingo passpoint.
@@ -150,14 +150,15 @@
 
         """
         langs = {
-          'CHT':"zh-TW",
-          'FRA':"fr-FR",
-          'US': "en-US",
-          'ARA': "ar-SA",
-          'SPA': "es-ES"
+            'CHT': "zh-TW",
+            'FRA': "fr-FR",
+            'US': "en-US",
+            'ARA': "ar-SA",
+            'SPA': "es-ES"
         }
         self.dut.ed.clear_all_events()
-        self.dut.adb.shell('settings put system system_locales %s ' % langs[lang])
+        self.dut.adb.shell('settings put system system_locales %s ' %
+                           langs[lang])
         self.dut.reboot()
         time.sleep(DEFAULT_TIMEOUT)
 
@@ -170,9 +171,8 @@
         """
         # Verify the boingo login page shows
         langtext = self.BOINGO_UI_TEXT[lang]
-        asserts.assert_true(
-            uutils.has_element(self.dut, text=langtext),
-            "Failed to launch boingohotspot login page")
+        asserts.assert_true(uutils.has_element(self.dut, text=langtext),
+                            "Failed to launch boingohotspot login page")
         # Go to the bottom of the page
         for _ in range(3):
             self.dut.adb.shell("input swipe 300 900 300 300")
@@ -187,7 +187,8 @@
                 time.sleep(2)
                 if index == 0:
                     #stop the ime launch
-                    self.dut.adb.shell("am force-stop com.google.android.inputmethod.latin")
+                    self.dut.adb.shell(
+                        "am force-stop com.google.android.inputmethod.latin")
                     self.dut.adb.shell("input text %s" % self.boingo_username)
                     index += 1
                 else:
@@ -195,7 +196,8 @@
                     break
                 self.dut.adb.shell("input keyevent 111")
         self.dut.adb.shell("input keyevent 111")  # collapse keyboard
-        self.dut.adb.shell("input swipe 300 900 300 750")  # swipe up to show text
+        self.dut.adb.shell(
+            "input swipe 300 900 300 750")  # swipe up to show text
 
         # Login
         uutils.wait_and_click(self.dut, text=PASSPOINT_BUTTON)
@@ -209,10 +211,9 @@
 
         """
         self.language_change(lang)
-        self.unpack_userparams(('osu_configs',))
+        self.unpack_userparams(('osu_configs', ))
         asserts.assert_true(
-            len(self.osu_configs) > 0,
-            "Need at least one osu config.")
+            len(self.osu_configs) > 0, "Need at least one osu config.")
         osu_config = self.osu_configs[OSU_BOINGO]
         # Clear all previous events.
         self.dut.ed.clear_all_events()
@@ -227,13 +228,11 @@
             if dut_event['data']['tag'] == 'failure':
                 raise signals.TestFailure(
                     "Passpoint Provisioning is failed with %s" %
-                    dut_event['data'][
-                        'reason'])
+                    dut_event['data']['reason'])
                 break
             if dut_event['data']['tag'] == 'status':
-                self.log.info(
-                    "Passpoint Provisioning status %s" % dut_event['data'][
-                        'status'])
+                self.log.info("Passpoint Provisioning status %s" %
+                              dut_event['data']['status'])
                 if int(dut_event['data']['status']) == 7:
                     time.sleep(DEFAULT_TIMEOUT)
                     self.ui_automator_boingo(lang)
@@ -243,11 +242,11 @@
         # Verify device connects to the Passpoint network.
         time.sleep(DEFAULT_TIMEOUT)
         current_passpoint = self.dut.droid.wifiGetConnectionInfo()
-        if current_passpoint[WifiEnums.SSID_KEY] not in osu_config[
-            "expected_ssids"]:
+        if current_passpoint[
+                WifiEnums.SSID_KEY] not in osu_config["expected_ssids"]:
             raise signals.TestFailure("Device did not connect to the %s"
-                                      " passpoint network" % osu_config[
-                                          "expected_ssids"])
+                                      " passpoint network" %
+                                      osu_config["expected_ssids"])
         self.get_configured_passpoint_and_delete()
         wutils.wait_for_disconnect(self.dut, timeout=15)
 
diff --git a/acts_tests/tests/google/wifi/WifiPasspointTest.py b/acts_tests/tests/google/wifi/WifiPasspointTest.py
index da8d28f..fe1ab9f 100755
--- a/acts_tests/tests/google/wifi/WifiPasspointTest.py
+++ b/acts_tests/tests/google/wifi/WifiPasspointTest.py
@@ -15,8 +15,6 @@
 #   limitations under the License.
 
 import itertools
-import pprint
-import queue
 import time
 
 from acts_contrib.test_utils.net import ui_utils as uutils
@@ -53,6 +51,7 @@
 PASSPOINT_BUTTON = "Get Passpoint"
 BOINGO_UI_TEXT = "Online Sign Up"
 
+
 class WifiPasspointTest(WifiBaseTest):
     """Tests for APIs in Android's WifiManager class.
 
@@ -66,17 +65,18 @@
         super().setup_class()
         self.dut = self.android_devices[0]
         wutils.wifi_test_device_init(self.dut)
-        req_params = ["passpoint_networks",
-                      "boingo_username",
-                      "boingo_password",]
-        self.unpack_userparams(req_param_names=req_params,)
+        req_params = [
+            "passpoint_networks",
+            "boingo_username",
+            "boingo_password",
+        ]
+        self.unpack_userparams(req_param_names=req_params, )
         asserts.assert_true(
             len(self.passpoint_networks) > 0,
             "Need at least one Passpoint network.")
         wutils.wifi_toggle_state(self.dut, True)
         self.unknown_fqdn = UNKNOWN_FQDN
 
-
     def setup_test(self):
         super().setup_test()
         self.dut.droid.wakeLockAcquireBright()
@@ -84,7 +84,6 @@
         self.dut.unlock_screen()
         self.dut.adb.shell("input keyevent KEYCODE_HOME")
 
-
     def teardown_test(self):
         super().teardown_test()
         self.dut.droid.wakeLockRelease()
@@ -94,10 +93,8 @@
             wutils.delete_passpoint(self.dut, config)
         wutils.reset_wifi(self.dut)
 
-
     """Helper Functions"""
 
-
     def install_passpoint_profile(self, passpoint_config):
         """Install the Passpoint network Profile.
 
@@ -105,13 +102,13 @@
             passpoint_config: A JSON dict of the Passpoint configuration.
 
         """
-        asserts.assert_true(WifiEnums.SSID_KEY in passpoint_config,
-                "Key '%s' must be present in network definition." %
-                WifiEnums.SSID_KEY)
+        asserts.assert_true(
+            WifiEnums.SSID_KEY in passpoint_config,
+            "Key '%s' must be present in network definition." %
+            WifiEnums.SSID_KEY)
         # Install the Passpoint profile.
         self.dut.droid.addUpdatePasspointConfig(passpoint_config)
 
-
     def check_passpoint_connection(self, passpoint_network):
         """Verify the device is automatically able to connect to the Passpoint
            network.
@@ -132,9 +129,8 @@
         self.log.info("Network Info: %s" % network_info)
         if not network_info or not network_info[WifiEnums.SSID_KEY] or \
             network_info[WifiEnums.SSID_KEY] not in passpoint_network:
-              raise signals.TestFailure(
-                  "Device did not connect to passpoint network.")
-
+            raise signals.TestFailure(
+                "Device did not connect to passpoint network.")
 
     def get_configured_passpoint_and_delete(self):
         """Get configured Passpoint network and delete using its FQDN."""
@@ -143,15 +139,15 @@
             raise signals.TestFailure("Failed to fetch the list of configured"
                                       "passpoint networks.")
         if not wutils.delete_passpoint(self.dut, passpoint_config[0]):
-            raise signals.TestFailure("Failed to delete Passpoint configuration"
-                                      " with FQDN = %s" % passpoint_config[0])
+            raise signals.TestFailure(
+                "Failed to delete Passpoint configuration"
+                " with FQDN = %s" % passpoint_config[0])
 
     def ui_automator_boingo(self):
         """Run UI automator for boingo passpoint."""
         # Verify the boingo login page shows
-        asserts.assert_true(
-            uutils.has_element(self.dut, text=BOINGO_UI_TEXT),
-            "Failed to launch boingohotspot login page")
+        asserts.assert_true(uutils.has_element(self.dut, text=BOINGO_UI_TEXT),
+                            "Failed to launch boingohotspot login page")
 
         # Go to the bottom of the page
         for _ in range(3):
@@ -173,7 +169,8 @@
                     break
                 self.dut.adb.shell("input keyevent 111")
         self.dut.adb.shell("input keyevent 111")  # collapse keyboard
-        self.dut.adb.shell("input swipe 300 900 300 750")  # swipe up to show text
+        self.dut.adb.shell(
+            "input swipe 300 900 300 750")  # swipe up to show text
 
         # Login
         uutils.wait_and_click(self.dut, text=PASSPOINT_BUTTON)
@@ -182,10 +179,9 @@
     def start_subscription_provisioning(self, state):
         """Start subscription provisioning with a default provider."""
 
-        self.unpack_userparams(('osu_configs',))
+        self.unpack_userparams(('osu_configs', ))
         asserts.assert_true(
-            len(self.osu_configs) > 0,
-            "Need at least one osu config.")
+            len(self.osu_configs) > 0, "Need at least one osu config.")
         osu_config = self.osu_configs[OSU_BOINGO]
         # Clear all previous events.
         self.dut.ed.clear_all_events()
@@ -208,13 +204,11 @@
             if dut_event['data']['tag'] == 'failure':
                 raise signals.TestFailure(
                     "Passpoint Provisioning is failed with %s" %
-                    dut_event['data'][
-                        'reason'])
+                    dut_event['data']['reason'])
                 break
             if dut_event['data']['tag'] == 'status':
-                self.log.info(
-                    "Passpoint Provisioning status %s" % dut_event['data'][
-                        'status'])
+                self.log.info("Passpoint Provisioning status %s" %
+                              dut_event['data']['status'])
                 if int(dut_event['data']['status']) == 7:
                     time.sleep(DEFAULT_TIMEOUT)
                     self.ui_automator_boingo()
@@ -224,11 +218,11 @@
         # Verify device connects to the Passpoint network.
         time.sleep(DEFAULT_TIMEOUT)
         current_passpoint = self.dut.droid.wifiGetConnectionInfo()
-        if current_passpoint[WifiEnums.SSID_KEY] not in osu_config[
-            "expected_ssids"]:
+        if current_passpoint[
+                WifiEnums.SSID_KEY] not in osu_config["expected_ssids"]:
             raise signals.TestFailure("Device did not connect to the %s"
-                                      " passpoint network" % osu_config[
-                                          "expected_ssids"])
+                                      " passpoint network" %
+                                      osu_config["expected_ssids"])
         # Delete the Passpoint profile.
         self.get_configured_passpoint_and_delete()
         wutils.wait_for_disconnect(self.dut, timeout=15)
@@ -236,10 +230,9 @@
     def start_subscription_provisioning_OSU_websie(self):
         """Start subscription provisioning with a default websider."""
 
-        self.unpack_userparams(('osu_configs',))
+        self.unpack_userparams(('osu_configs', ))
         asserts.assert_true(
-            len(self.osu_configs) > 0,
-            "Need at least one osu config.")
+            len(self.osu_configs) > 0, "Need at least one osu config.")
         osu_config = self.osu_configs[OSU_BOINGO]
         # Clear all previous events.
         self.dut.ed.clear_all_events()
@@ -254,13 +247,11 @@
             if dut_event['data']['tag'] == 'failure':
                 raise signals.TestFailure(
                     "Passpoint Provisioning is failed with %s" %
-                    dut_event['data'][
-                        'reason'])
+                    dut_event['data']['reason'])
                 break
             if dut_event['data']['tag'] == 'status':
-                self.log.info(
-                    "Passpoint Provisioning status %s" % dut_event['data'][
-                        'status'])
+                self.log.info("Passpoint Provisioning status %s" %
+                              dut_event['data']['status'])
                 if int(dut_event['data']['status']) == 7:
                     time.sleep(DEFAULT_TIMEOUT)
                     asserts.assert_true(
@@ -270,7 +261,6 @@
         # Clear all previous events.
         self.dut.ed.clear_all_events()
 
-
     """Tests"""
 
     @test_tracker_info(uuid="b0bc0153-77bb-4594-8f19-cea2c6bd2f43")
@@ -292,7 +282,6 @@
         self.get_configured_passpoint_and_delete()
         wutils.wait_for_disconnect(self.dut)
 
-
     @test_tracker_info(uuid="eb29d6e2-a755-4c9c-9e4e-63ea2277a64a")
     def test_update_passpoint_network(self):
         """Update a previous Passpoint network and verify device still connects
@@ -324,7 +313,6 @@
         self.get_configured_passpoint_and_delete()
         wutils.wait_for_disconnect(self.dut)
 
-
     @test_tracker_info(uuid="b6e8068d-faa1-49f2-b421-c60defaed5f0")
     def test_add_delete_list_of_passpoint_network(self):
         """Add multiple passpoint networks, list them and delete one by one.
@@ -340,7 +328,8 @@
             time.sleep(DEFAULT_TIMEOUT)
         configs = self.dut.droid.getPasspointConfigs()
         #  It is length -1 because ATT profile will be handled separately
-        if not len(configs) or len(configs) != len(self.passpoint_networks[:2]):
+        if not len(configs) or len(configs) != len(
+                self.passpoint_networks[:2]):
             raise signals.TestFailure("Failed to fetch some or all of the"
                                       " configured passpoint networks.")
         for config in configs:
@@ -349,7 +338,6 @@
                                           " configuration with FQDN = %s" %
                                           config)
 
-
     @test_tracker_info(uuid="a53251be-7aaf-41fc-a5f3-63984269d224")
     def test_delete_unknown_fqdn(self):
         """Negative test to delete Passpoint profile using an unknown FQDN.
@@ -378,7 +366,6 @@
         self.get_configured_passpoint_and_delete()
         wutils.wait_for_disconnect(self.dut)
 
-
     @test_tracker_info(uuid="bf03c03a-e649-4e2b-a557-1f791bd98951")
     def test_passpoint_failover(self):
         """Add a pair of passpoint networks and test failover when one of the"
@@ -403,12 +390,12 @@
         current_passpoint = self.dut.droid.wifiGetConnectionInfo()
         current_ssid = current_passpoint[WifiEnums.SSID_KEY]
         if current_ssid not in passpoint_ssid:
-           raise signals.TestFailure("Device did not connect to any of the "
-                                     "configured Passpoint networks."
-                                     "current: {}, expected: {}"
-                                     .format(current_ssid, passpoint_ssid))
+            raise signals.TestFailure("Device did not connect to any of the "
+                                      "configured Passpoint networks."
+                                      "current: {}, expected: {}".format(
+                                          current_ssid, passpoint_ssid))
 
-        expected_ssid =  self.passpoint_networks[0][WifiEnums.SSID_KEY]
+        expected_ssid = self.passpoint_networks[0][WifiEnums.SSID_KEY]
         if current_ssid in expected_ssid:
             expected_ssid = self.passpoint_networks[1][WifiEnums.SSID_KEY]
 
@@ -431,7 +418,6 @@
         self.get_configured_passpoint_and_delete()
         wutils.wait_for_disconnect(self.dut)
 
-
     @test_tracker_info(uuid="37ae0223-0cb7-43f3-8ba8-474fad6e4b71")
     def test_install_att_passpoint_profile(self):
         """Add an AT&T Passpoint profile.
@@ -447,7 +433,6 @@
         if not isFound:
             raise signals.TestFailure("cannot find ATT profile.")
 
-
     @test_tracker_info(uuid="e3e826d2-7c39-4c37-ab3f-81992d5aa0e8")
     def test_att_passpoint_network(self):
         """Add a AT&T Passpoint network and verify device connects to it.
@@ -472,17 +457,14 @@
         self.get_configured_passpoint_and_delete()
         wutils.wait_for_disconnect(self.dut)
 
-
     @test_tracker_info(uuid="c85c81b2-7133-4635-8328-9498169ae802")
     def test_start_subscription_provisioning(self):
         self.start_subscription_provisioning(0)
 
-
     @test_tracker_info(uuid="fd09a643-0d4b-45a9-881a-a771f9707ab1")
     def test_start_subscription_provisioning_and_reset_wifi(self):
         self.start_subscription_provisioning(RESET)
 
-
     @test_tracker_info(uuid="f43ea759-673f-4567-aa11-da3bc2cabf08")
     def test_start_subscription_provisioning_and_toggle_wifi(self):
         self.start_subscription_provisioning(TOGGLE)
@@ -503,7 +485,8 @@
         ssid = passpoint_config[WifiEnums.SSID_KEY]
         self.check_passpoint_connection(ssid)
         self.dut.log.info("Disable auto join on passpoint")
-        self.dut.droid.wifiEnableAutojoinPasspoint(passpoint_config['fqdn'], False)
+        self.dut.droid.wifiEnableAutojoinPasspoint(passpoint_config['fqdn'],
+                                                   False)
         wutils.wifi_toggle_state(self.dut, False)
         wutils.wifi_toggle_state(self.dut, True)
         asserts.assert_false(
@@ -531,8 +514,9 @@
         # Clear all previous events.
         self.dut.ed.clear_all_events()
         current_passpoint = self.dut.droid.wifiGetConnectionInfo()
-        asserts.assert_false(current_passpoint[WifiEnums.SSID_KEY] in osu_config[
-            "expected_ssids"], "Device should not connect")
+        asserts.assert_false(
+            current_passpoint[WifiEnums.SSID_KEY] in
+            osu_config["expected_ssids"], "Device should not connect")
 
         # Verify device does not connects to the Passpoint network.
         asserts.assert_true(
@@ -561,7 +545,8 @@
             wifi_constants.WIFI_CONNECTED, "DUT did not connect to passpoint.")
 
     @test_tracker_info(uuid="3372c605-2934-4739-8413-d9a103e87eef")
-    def test_passpoint_release_2_subscription_detail_of_current_connected_network(self):
+    def test_passpoint_release_2_subscription_detail_of_current_connected_network(
+            self):
         """Add a Passpoint network and check the connection detail
         Steps:
             1. Connecting a passpoint wifi
@@ -571,10 +556,9 @@
             1.There is a forget button shown on the UI.
             2.The dut shows Frequency, Security and Advanced (Metered, Privacy, Network detail, etc.
         """
-        self.unpack_userparams(('osu_configs',))
+        self.unpack_userparams(('osu_configs', ))
         asserts.assert_true(
-            len(self.osu_configs) > 0,
-            "Need at least one osu config.")
+            len(self.osu_configs) > 0, "Need at least one osu config.")
         osu_config = self.osu_configs[OSU_BOINGO]
         # Clear all previous events.
         self.dut.ed.clear_all_events()
@@ -589,13 +573,11 @@
             if dut_event['data']['tag'] == 'failure':
                 raise signals.TestFailure(
                     "Passpoint Provisioning is failed with %s" %
-                    dut_event['data'][
-                        'reason'])
+                    dut_event['data']['reason'])
                 break
             if dut_event['data']['tag'] == 'status':
-                self.log.info(
-                    "Passpoint Provisioning status %s" % dut_event['data'][
-                        'status'])
+                self.log.info("Passpoint Provisioning status %s" %
+                              dut_event['data']['status'])
                 if int(dut_event['data']['status']) == 7:
                     time.sleep(DEFAULT_TIMEOUT)
                     self.ui_automator_boingo()
@@ -606,11 +588,11 @@
         # Verify device connects to the Passpoint network.
         time.sleep(DEFAULT_TIMEOUT)
         current_passpoint = self.dut.droid.wifiGetConnectionInfo()
-        if current_passpoint[WifiEnums.SSID_KEY] not in osu_config[
-            "expected_ssids"]:
+        if current_passpoint[
+                WifiEnums.SSID_KEY] not in osu_config["expected_ssids"]:
             raise signals.TestFailure("Device did not connect to the %s"
-                                      " passpoint network" % osu_config[
-                                          "expected_ssids"])
+                                      " passpoint network" %
+                                      osu_config["expected_ssids"])
         self.dut.adb.shell("am start-activity -a\
             android.settings.NETWORK_PROVIDER_SETTINGS")
 
@@ -623,16 +605,19 @@
         asserts.assert_true(
             uutils.has_element(self.dut, content_desc="Network details"),
             "Failed to launch Network details")
-        asserts.assert_true(uutils.has_element(self.dut,
-                                               text_contains = "Connected / Metered"),
-                            "failed to find the Connected text")
-        asserts.assert_true(uutils.has_element(self.dut, text_contains ="Passpoint"),
-                            "failed to find the Passpoint secuty type")
-        asserts.assert_true(uutils.has_element(self.dut, text_contains = "Forget"),
-                            "failed to find the Forget button")
-        asserts.assert_true(uutils.has_element(self.dut, text_contains ="5 GHz"),
-                            "failed to find the Frequency text")
+        asserts.assert_true(
+            uutils.has_element(self.dut, text_contains="Connected / Metered"),
+            "failed to find the Connected text")
+        asserts.assert_true(
+            uutils.has_element(self.dut, text_contains="Passpoint"),
+            "failed to find the Passpoint secuty type")
+        asserts.assert_true(
+            uutils.has_element(self.dut, text_contains="Forget"),
+            "failed to find the Forget button")
+        asserts.assert_true(
+            uutils.has_element(self.dut, text_contains="5 GHz"),
+            "failed to find the Frequency text")
 
         # Delete the Passpoint profile.
         self.get_configured_passpoint_and_delete()
-        wutils.wait_for_disconnect(self.dut, timeout=15)
\ No newline at end of file
+        wutils.wait_for_disconnect(self.dut, timeout=15)
diff --git a/acts_tests/tests/google/wifi/WifiPingTest.py b/acts_tests/tests/google/wifi/WifiPingTest.py
index 096a935..ca1ccd7 100644
--- a/acts_tests/tests/google/wifi/WifiPingTest.py
+++ b/acts_tests/tests/google/wifi/WifiPingTest.py
@@ -504,26 +504,56 @@
         """
         # Configure AP
         self.setup_ap(testcase_params)
-        # Set attenuator to 0 dB
+        # Set attenuator to starting attenuation
+        band = wputils.CHANNEL_TO_BAND_MAP[testcase_params['channel']]
         for attenuator in self.attenuators:
-            attenuator.set_atten(testcase_params['atten_range'][0],
-                                 strict=False,
-                                 retry=True)
+            attenuator.set_atten(
+                self.testclass_params['range_atten_start'].get(band, 0),
+                strict=False,
+                retry=True)
         # Reset, configure, and connect DUT
         self.setup_dut(testcase_params)
 
     def get_range_start_atten(self, testcase_params):
         """Gets the starting attenuation for this ping test.
 
-        This function is used to get the starting attenuation for ping range
-        tests. This implementation returns the default starting attenuation,
-        however, defining this function enables a more involved configuration
-        for over-the-air test classes.
+        The function gets the starting attenuation by checking whether a test
+        at the same configuration has executed. If so it sets the starting
+        point a configurable number of dBs below the reference test.
 
         Args:
-            testcase_params: dict containing all test params
+            testcase_params: dict containing all test parameters
+        Returns:
+            start_atten: starting attenuation for current test
         """
-        return self.testclass_params['range_atten_start']
+        band = wputils.CHANNEL_TO_BAND_MAP[testcase_params['channel']]
+        # If the test is being retried, start from the beginning
+        if self.retry_flag:
+            self.log.info('Retry flag set. Setting attenuation to minimum.')
+            return self.testclass_params['range_atten_start'].get(band, 0)
+        # Get the current and reference test config. The reference test is the
+        # one performed at the current MCS+1
+        ref_test_params = wputils.extract_sub_dict(
+            testcase_params, testcase_params['reference_params'])
+        # Check if reference test has been run and set attenuation accordingly
+        previous_params = [
+            wputils.extract_sub_dict(result['testcase_params'],
+                                     testcase_params['reference_params'])
+            for result in self.testclass_results
+        ]
+        try:
+            ref_index = previous_params[::-1].index(ref_test_params)
+            ref_index = len(previous_params) - 1 - ref_index
+            start_atten = self.testclass_results[ref_index][
+                'atten_at_range'] - (
+                    self.testclass_params['adjacent_range_test_gap'])
+        except ValueError:
+            start_atten = self.testclass_params['range_atten_start'].get(
+                band, 0)
+            self.log.info(
+                'Reference test not found. Starting from {} dB'.format(
+                    start_atten))
+        return start_atten
 
     def compile_test_params(self, testcase_params):
         # Check if test should be skipped.
@@ -534,6 +564,7 @@
         band = self.access_point.band_lookup_by_channel(
             testcase_params['channel'])
         testcase_params['test_network'] = self.main_network[band]
+        testcase_params['band'] = band
         if testcase_params['chain_mask'] in ['0', '1']:
             testcase_params['attenuated_chain'] = 'DUT-Chain-{}'.format(
                 1 if testcase_params['chain_mask'] == '0' else 0)
@@ -595,7 +626,7 @@
         self.pass_fail_check(ping_result)
 
     def generate_test_cases(self, ap_power, channels, modes, chain_mask,
-                            test_types):
+                            test_types, **kwargs):
         """Function that auto-generates test cases for a test class."""
         test_cases = []
         allowed_configs = {
@@ -620,7 +651,8 @@
                                                       channel=channel,
                                                       mode=mode,
                                                       bandwidth=bandwidth,
-                                                      chain_mask=chain)
+                                                      chain_mask=chain,
+                                                      **kwargs)
             setattr(self, testcase_name,
                     partial(self._test_ping, testcase_params))
             test_cases.append(testcase_name)
@@ -628,46 +660,50 @@
 
 
 class WifiPing_TwoChain_Test(WifiPingTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
-        self.tests = self.generate_test_cases(ap_power='standard',
-                                              channels=[
-                                                  1, 6, 11, 36, 40, 44, 48,
-                                                  149, 153, 157, 161, '6g37',
-                                                  '6g117', '6g213'
-                                              ],
-                                              modes=['bw20', 'bw40', 'bw80'],
-                                              test_types=[
-                                                  'test_ping_range',
-                                                  'test_fast_ping_rtt',
-                                                  'test_slow_ping_rtt'
-                                              ],
-                                              chain_mask=['2x2'])
+        self.tests = self.generate_test_cases(
+            ap_power='standard',
+            channels=[
+                1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161, '6g37', '6g117',
+                '6g213'
+            ],
+            modes=['bw20', 'bw80', 'bw160'],
+            test_types=[
+                'test_ping_range', 'test_fast_ping_rtt', 'test_slow_ping_rtt'
+            ],
+            chain_mask=['2x2'],
+            reference_params=['band', 'chain_mask'])
 
 
 class WifiPing_PerChainRange_Test(WifiPingTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
-        self.tests = self.generate_test_cases(ap_power='standard',
-                                              chain_mask=['0', '1', '2x2'],
-                                              channels=[
-                                                  1, 6, 11, 36, 40, 44, 48,
-                                                  149, 153, 157, 161, '6g37',
-                                                  '6g117', '6g213'
-                                              ],
-                                              modes=['bw20', 'bw40', 'bw80'],
-                                              test_types=['test_ping_range'])
+        self.tests = self.generate_test_cases(
+            ap_power='standard',
+            chain_mask=['0', '1', '2x2'],
+            channels=[
+                1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161, '6g37', '6g117',
+                '6g213'
+            ],
+            modes=['bw20', 'bw80', 'bw160'],
+            test_types=['test_ping_range'],
+            reference_params=['band', 'chain_mask'])
 
 
 class WifiPing_LowPowerAP_Test(WifiPingTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
             ap_power='low_power',
             chain_mask=['0', '1', '2x2'],
             channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
-            modes=['bw20', 'bw40', 'bw80'],
-            test_types=['test_ping_range'])
+            modes=['bw20', 'bw80'],
+            test_types=['test_ping_range'],
+            reference_params=['band', 'chain_mask'])
 
 
 # Over-the air version of ping tests
@@ -678,6 +714,7 @@
     setting turntable orientation and other chamber parameters to study
     performance in varying channel conditions
     """
+
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
@@ -788,45 +825,8 @@
         # Continue setting up ping test
         WifiPingTest.setup_ping_test(self, testcase_params)
 
-    def get_range_start_atten(self, testcase_params):
-        """Gets the starting attenuation for this ping test.
-
-        The function gets the starting attenuation by checking whether a test
-        at the same configuration has executed. If so it sets the starting
-        point a configurable number of dBs below the reference test.
-
-        Returns:
-            start_atten: starting attenuation for current test
-        """
-        # If the test is being retried, start from the beginning
-        if self.retry_flag:
-            self.log.info('Retry flag set. Setting attenuation to minimum.')
-            return self.testclass_params['range_atten_start']
-        # Get the current and reference test config. The reference test is the
-        # one performed at the current MCS+1
-        ref_test_params = wputils.extract_sub_dict(
-            testcase_params, ['channel', 'mode', 'chain_mask'])
-        # Check if reference test has been run and set attenuation accordingly
-        previous_params = [
-            wputils.extract_sub_dict(result['testcase_params'],
-                                     ['channel', 'mode', 'chain_mask'])
-            for result in self.testclass_results
-        ]
-        try:
-            ref_index = previous_params[::-1].index(ref_test_params)
-            ref_index = len(previous_params) - 1 - ref_index
-            start_atten = self.testclass_results[ref_index][
-                'atten_at_range'] - (
-                    self.testclass_params['adjacent_range_test_gap'])
-        except ValueError:
-            self.log.info(
-                'Reference test not found. Starting from {} dB'.format(
-                    self.testclass_params['range_atten_start']))
-            start_atten = self.testclass_params['range_atten_start']
-        return start_atten
-
     def generate_test_cases(self, ap_power, channels, modes, chain_masks,
-                            chamber_mode, positions):
+                            chamber_mode, positions, **kwargs):
         test_cases = []
         allowed_configs = {
             20: [
@@ -853,7 +853,8 @@
                 chain_mask=chain_mask,
                 chamber_mode=chamber_mode,
                 total_positions=len(positions),
-                position=position)
+                position=position,
+                **kwargs)
             setattr(self, testcase_name,
                     partial(self._test_ping, testcase_params))
             test_cases.append(testcase_name)
@@ -861,6 +862,7 @@
 
 
 class WifiOtaPing_TenDegree_Test(WifiOtaPingTest):
+
     def __init__(self, controllers):
         WifiOtaPingTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(
@@ -869,49 +871,57 @@
             modes=['bw20'],
             chain_masks=['2x2'],
             chamber_mode='orientation',
-            positions=list(range(0, 360, 10)))
+            positions=list(range(0, 360, 10)),
+            reference_params=['channel', 'mode', 'chain_mask'])
 
 
 class WifiOtaPing_45Degree_Test(WifiOtaPingTest):
+
     def __init__(self, controllers):
         WifiOtaPingTest.__init__(self, controllers)
-        self.tests = self.generate_test_cases(ap_power='standard',
-                                              channels=[
-                                                  1, 6, 11, 36, 40, 44, 48,
-                                                  149, 153, 157, 161, '6g37',
-                                                  '6g117', '6g213'
-                                              ],
-                                              modes=['bw20'],
-                                              chain_masks=['2x2'],
-                                              chamber_mode='orientation',
-                                              positions=list(range(0, 360,
-                                                                   45)))
+        self.tests = self.generate_test_cases(
+            ap_power='standard',
+            channels=[
+                1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161, '6g37', '6g117',
+                '6g213'
+            ],
+            modes=['bw20'],
+            chain_masks=['2x2'],
+            chamber_mode='orientation',
+            positions=list(range(0, 360, 45)),
+            reference_params=['channel', 'mode', 'chain_mask'])
 
 
 class WifiOtaPing_SteppedStirrers_Test(WifiOtaPingTest):
+
     def __init__(self, controllers):
         WifiOtaPingTest.__init__(self, controllers)
-        self.tests = self.generate_test_cases(ap_power='standard',
-                                              channels=[6, 36, 149],
-                                              modes=['bw20'],
-                                              chain_masks=['2x2'],
-                                              chamber_mode='stepped stirrers',
-                                              positions=list(range(100)))
+        self.tests = self.generate_test_cases(
+            ap_power='standard',
+            channels=[6, 36, 149],
+            modes=['bw20'],
+            chain_masks=['2x2'],
+            chamber_mode='stepped stirrers',
+            positions=list(range(100)),
+            reference_params=['channel', 'mode', 'chain_mask'])
 
 
 class WifiOtaPing_LowPowerAP_TenDegree_Test(WifiOtaPingTest):
+
     def __init__(self, controllers):
         WifiOtaPingTest.__init__(self, controllers)
-        self.tests = self.generate_test_cases(ap_power='low_power',
-                                              channels=[6, 36, 149],
-                                              modes=['bw20'],
-                                              chain_masks=['2x2'],
-                                              chamber_mode='orientation',
-                                              positions=list(range(0, 360,
-                                                                   10)))
+        self.tests = self.generate_test_cases(
+            ap_power='low_power',
+            channels=[6, 36, 149],
+            modes=['bw20'],
+            chain_masks=['2x2'],
+            chamber_mode='orientation',
+            positions=list(range(0, 360, 10)),
+            reference_params=['channel', 'mode', 'chain_mask'])
 
 
 class WifiOtaPing_LowPowerAP_45Degree_Test(WifiOtaPingTest):
+
     def __init__(self, controllers):
         WifiOtaPingTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(
@@ -920,33 +930,40 @@
             modes=['bw20'],
             chain_masks=['2x2'],
             chamber_mode='orientation',
-            positions=list(range(0, 360, 45)))
+            positions=list(range(0, 360, 45)),
+            reference_params=['channel', 'mode', 'chain_mask'])
 
 
 class WifiOtaPing_LowPowerAP_SteppedStirrers_Test(WifiOtaPingTest):
+
     def __init__(self, controllers):
         WifiOtaPingTest.__init__(self, controllers)
-        self.tests = self.generate_test_cases(ap_power='low_power',
-                                              channels=[6, 36, 149],
-                                              modes=['bw20'],
-                                              chain_masks=['2x2'],
-                                              chamber_mode='stepped stirrers',
-                                              positions=list(range(100)))
+        self.tests = self.generate_test_cases(
+            ap_power='low_power',
+            channels=[6, 36, 149],
+            modes=['bw20'],
+            chain_masks=['2x2'],
+            chamber_mode='stepped stirrers',
+            positions=list(range(100)),
+            reference_params=['channel', 'mode', 'chain_mask'])
 
 
 class WifiOtaPing_LowPowerAP_PerChain_TenDegree_Test(WifiOtaPingTest):
+
     def __init__(self, controllers):
         WifiOtaPingTest.__init__(self, controllers)
-        self.tests = self.generate_test_cases(ap_power='low_power',
-                                              channels=[6, 36, 149],
-                                              modes=['bw20'],
-                                              chain_masks=[0, 1, '2x2'],
-                                              chamber_mode='orientation',
-                                              positions=list(range(0, 360,
-                                                                   10)))
+        self.tests = self.generate_test_cases(
+            ap_power='low_power',
+            channels=[6, 36, 149],
+            modes=['bw20'],
+            chain_masks=[0, 1, '2x2'],
+            chamber_mode='orientation',
+            positions=list(range(0, 360, 10)),
+            reference_params=['channel', 'mode', 'chain_mask'])
 
 
 class WifiOtaPing_PerChain_TenDegree_Test(WifiOtaPingTest):
+
     def __init__(self, controllers):
         WifiOtaPingTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(
@@ -955,4 +972,5 @@
             modes=['bw20'],
             chain_masks=[0, 1, '2x2'],
             chamber_mode='orientation',
-            positions=list(range(0, 360, 10)))
+            positions=list(range(0, 360, 10)),
+            reference_params=['channel', 'mode', 'chain_mask'])
diff --git a/acts_tests/tests/google/wifi/WifiRoamingTest.py b/acts_tests/tests/google/wifi/WifiRoamingTest.py
index 2da901c..7bce78b 100644
--- a/acts_tests/tests/google/wifi/WifiRoamingTest.py
+++ b/acts_tests/tests/google/wifi/WifiRoamingTest.py
@@ -37,7 +37,7 @@
 
         self.dut = self.android_devices[0]
         self.dut_client = self.android_devices[1]
-        req_params = ["roaming_attn"]
+        req_params = ["roaming_attn", "sae_roaming_unspported_list"]
         self.unpack_userparams(req_param_names=req_params,)
         self.country_code = wutils.WifiEnums.CountryCode.US
         if hasattr(self, "country_code_file"):
@@ -64,6 +64,9 @@
         self.dut.ed.clear_all_events()
         self.dut.droid.wakeLockAcquireBright()
         self.dut.droid.wakeUpNow()
+        asserts.skip_if("sae" in self.test_name and
+                        self.dut.model in self.sae_roaming_unspported_list,
+                        "%s doesn't support wpa3 roaming." % self.dut.model)
 
     def teardown_test(self):
         super().teardown_test()
diff --git a/acts_tests/tests/google/wifi/WifiRssiTest.py b/acts_tests/tests/google/wifi/WifiRssiTest.py
index 06eed43..452591f 100644
--- a/acts_tests/tests/google/wifi/WifiRssiTest.py
+++ b/acts_tests/tests/google/wifi/WifiRssiTest.py
@@ -53,6 +53,7 @@
     configurable attenuation waveforms.For an example config file to run this
     test class see example_connectivity_performance_ap_sta.json.
     """
+
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
@@ -494,12 +495,12 @@
             thread_future = wputils.get_ping_stats_nb(
                 self.remote_server, self.dut_ip,
                 testcase_params['traffic_timeout'], 0.5, 64)
+        llstats_obj.update_stats()
         for atten in testcase_params['rssi_atten_range']:
             # Set Attenuation
             self.log.info('Setting attenuation to {} dB'.format(atten))
             for attenuator in self.attenuators:
                 attenuator.set_atten(atten)
-            llstats_obj.update_stats()
             current_rssi = collections.OrderedDict()
             current_rssi = wputils.get_connected_rssi(
                 self.dut, testcase_params['connected_measurements'],
@@ -633,9 +634,13 @@
             testclass_params['rssi_vs_atten_connected_measurements'],
             scan_measurements=self.
             testclass_params['rssi_vs_atten_scan_measurements'],
-            first_measurement_delay=MED_SLEEP,
-            rssi_under_test=self.testclass_params['rssi_vs_atten_metrics'],
+            first_measurement_delay=SHORT_SLEEP,
             absolute_accuracy=1)
+        rssi_under_test = self.testclass_params['rssi_vs_atten_metrics']
+        if self.testclass_params[
+                'rssi_vs_atten_scan_measurements'] == 0 and 'scan_rssi' in rssi_under_test:
+            rssi_under_test.remove('scan_rssi')
+        testcase_params['rssi_under_test'] = rssi_under_test
 
         testcase_params['band'] = self.access_point.band_lookup_by_channel(
             testcase_params['channel'])
@@ -677,7 +682,7 @@
                 self.testclass_params['rssi_stability_duration'] /
                 self.testclass_params['polling_frequency']),
             scan_measurements=0,
-            first_measurement_delay=MED_SLEEP,
+            first_measurement_delay=SHORT_SLEEP,
             rssi_atten_range=self.testclass_params['rssi_stability_atten'])
         testcase_params['band'] = self.access_point.band_lookup_by_channel(
             testcase_params['channel'])
@@ -846,6 +851,7 @@
 
 
 class WifiRssi_2GHz_ActiveTraffic_Test(WifiRssiTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -854,6 +860,7 @@
 
 
 class WifiRssi_5GHz_ActiveTraffic_Test(WifiRssiTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -863,6 +870,7 @@
 
 
 class WifiRssi_AllChannels_ActiveTraffic_Test(WifiRssiTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -873,6 +881,7 @@
 
 
 class WifiRssi_SampleChannels_NoTraffic_Test(WifiRssiTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -881,6 +890,7 @@
 
 
 class WifiRssiTrackingTest(WifiRssiTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(['test_rssi_tracking'],
@@ -897,6 +907,7 @@
     It allows setting orientation and other chamber parameters to study
     performance in varying channel conditions
     """
+
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
@@ -1027,7 +1038,7 @@
         testcase_params.update(connected_measurements=int(
             rssi_test_duration / self.testclass_params['polling_frequency']),
                                scan_measurements=0,
-                               first_measurement_delay=MED_SLEEP,
+                               first_measurement_delay=SHORT_SLEEP,
                                rssi_atten_range=rssi_ota_test_attenuation)
         testcase_params['band'] = self.access_point.band_lookup_by_channel(
             testcase_params['channel'])
@@ -1100,6 +1111,7 @@
 
 
 class WifiOtaRssi_Accuracy_Test(WifiOtaRssiTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(['test_rssi_vs_atten'],
@@ -1110,6 +1122,7 @@
 
 
 class WifiOtaRssi_StirrerVariation_Test(WifiOtaRssiTest):
+
     def __init__(self, controllers):
         WifiRssiTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(['test_rssi_variation'],
@@ -1119,6 +1132,7 @@
 
 
 class WifiOtaRssi_TenDegree_Test(WifiOtaRssiTest):
+
     def __init__(self, controllers):
         WifiRssiTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(['test_rssi_over_orientation'],
diff --git a/acts_tests/tests/google/wifi/WifiRvrTest.py b/acts_tests/tests/google/wifi/WifiRvrTest.py
index ffa52d5..4a64552 100644
--- a/acts_tests/tests/google/wifi/WifiRvrTest.py
+++ b/acts_tests/tests/google/wifi/WifiRvrTest.py
@@ -137,14 +137,23 @@
                     primary_y_label='Throughput (Mbps)')
             plots[plot_id].add_line(result['total_attenuation'],
                                     result['throughput_receive'],
-                                    result['test_name'],
+                                    result['test_name'].strip('test_rvr_'),
                                     hover_text=result['hover_text'],
                                     marker='circle')
             plots[plot_id].add_line(result['total_attenuation'],
-                                    result['avg_phy_rate'],
-                                    result['test_name'] + ' (PHY)',
+                                    result['rx_phy_rate'],
+                                    result['test_name'].strip('test_rvr_') +
+                                    ' (Rx PHY)',
                                     hover_text=result['hover_text'],
-                                    marker='circle')
+                                    style='dashed',
+                                    marker='inverted_triangle')
+            plots[plot_id].add_line(result['total_attenuation'],
+                                    result['tx_phy_rate'],
+                                    result['test_name'].strip('test_rvr_') +
+                                    ' (Tx PHY)',
+                                    hover_text=result['hover_text'],
+                                    style='dashed',
+                                    marker='triangle')
 
         figure_list = []
         for plot_id, plot in plots.items():
@@ -311,39 +320,36 @@
                 ) for rssi in rvr_result['rssi']
             ]
         }
-        if 'DL' in self.current_test_name:
-            rvr_result['avg_phy_rate'] = [
-                curr_llstats['summary'].get('mean_rx_phy_rate', 0)
-                for curr_llstats in rvr_result['llstats']
-            ]
-        else:
-            rvr_result['avg_phy_rate'] = [
-                curr_llstats['summary'].get('mean_tx_phy_rate', 0)
-                for curr_llstats in rvr_result['llstats']
-            ]
+
         figure.add_line(rvr_result['total_attenuation'],
                         rvr_result['throughput_receive'],
                         'Measured Throughput',
                         hover_text=rvr_result['hover_text'],
-                        color='red',
+                        color='black',
                         marker='circle')
-        rvr_result['avg_phy_rate'].extend(
-            [0] * (len(rvr_result['total_attenuation']) -
-                   len(rvr_result['avg_phy_rate'])))
-        figure.add_line(rvr_result['total_attenuation'],
-                        rvr_result['avg_phy_rate'],
-                        'Average PHY Rate',
-                        hover_text=rvr_result['hover_text'],
-                        color='red',
-                        style='dashed',
-                        marker='square')
+        figure.add_line(
+            rvr_result['total_attenuation'][0:len(rvr_result['rx_phy_rate'])],
+            rvr_result['rx_phy_rate'],
+            'Rx PHY Rate',
+            hover_text=rvr_result['hover_text'],
+            color='blue',
+            style='dashed',
+            marker='inverted_triangle')
+        figure.add_line(
+            rvr_result['total_attenuation'][0:len(rvr_result['rx_phy_rate'])],
+            rvr_result['tx_phy_rate'],
+            'Tx PHY Rate',
+            hover_text=rvr_result['hover_text'],
+            color='red',
+            style='dashed',
+            marker='triangle')
 
         output_file_path = os.path.join(
             self.log_path, '{}.html'.format(self.current_test_name))
         figure.generate_figure(output_file_path)
 
     def compute_test_metrics(self, rvr_result):
-        #Set test metrics
+        # Set test metrics
         rvr_result['metrics'] = {}
         rvr_result['metrics']['peak_tput'] = max(
             rvr_result['throughput_receive'])
@@ -362,7 +368,7 @@
         for idx in range(len(tput_below_limit)):
             if all(tput_below_limit[idx:]):
                 if idx == 0:
-                    #Throughput was never above limit
+                    # Throughput was never above limit
                     rvr_result['metrics']['high_tput_range'] = -1
                 else:
                     rvr_result['metrics']['high_tput_range'] = rvr_result[
@@ -411,6 +417,8 @@
             self.testclass_params.get('monitor_llstats', 1))
         zero_counter = 0
         throughput = []
+        rx_phy_rate = []
+        tx_phy_rate = []
         llstats = []
         rssi = []
         for atten in testcase_params['atten_range']:
@@ -479,6 +487,10 @@
             llstats_obj.update_stats()
             curr_llstats = llstats_obj.llstats_incremental.copy()
             llstats.append(curr_llstats)
+            rx_phy_rate.append(curr_llstats['summary'].get(
+                'mean_rx_phy_rate', 0))
+            tx_phy_rate.append(curr_llstats['summary'].get(
+                'mean_tx_phy_rate', 0))
             self.log.info(
                 ('Throughput at {0:.2f} dB is {1:.2f} Mbps. '
                  'RSSI = {2:.2f} [{3:.2f}, {4:.2f}].').format(
@@ -492,9 +504,11 @@
             if zero_counter == self.MAX_CONSECUTIVE_ZEROS:
                 self.log.info(
                     'Throughput stable at 0 Mbps. Stopping test now.')
-                throughput.extend(
-                    [0] *
-                    (len(testcase_params['atten_range']) - len(throughput)))
+                zero_padding = len(
+                    testcase_params['atten_range']) - len(throughput)
+                throughput.extend([0] * zero_padding)
+                rx_phy_rate.extend([0] * zero_padding)
+                tx_phy_rate.extend([0] * zero_padding)
                 break
         for attenuator in self.attenuators:
             attenuator.set_atten(0, strict=False, retry=True)
@@ -512,6 +526,8 @@
         ]
         rvr_result['rssi'] = rssi
         rvr_result['throughput_receive'] = throughput
+        rvr_result['rx_phy_rate'] = rx_phy_rate
+        rvr_result['tx_phy_rate'] = tx_phy_rate
         rvr_result['llstats'] = llstats
         return rvr_result
 
@@ -556,8 +572,9 @@
             self.sta_dut.droid.wakeLockAcquireDim()
         else:
             self.sta_dut.go_to_sleep()
-        if wputils.validate_network(self.sta_dut,
-                                    testcase_params['test_network']['SSID']):
+        if (wputils.validate_network(self.sta_dut,
+                                     testcase_params['test_network']['SSID'])
+                and not self.testclass_params.get('force_reconnect', 0)):
             self.log.info('Already connected to desired network')
         else:
             wutils.wifi_toggle_state(self.sta_dut, False)
@@ -733,6 +750,7 @@
 
 
 class WifiRvr_TCP_Test(WifiRvrTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -746,6 +764,7 @@
 
 
 class WifiRvr_VHT_TCP_Test(WifiRvrTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -756,6 +775,7 @@
 
 
 class WifiRvr_HE_TCP_Test(WifiRvrTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -769,6 +789,7 @@
 
 
 class WifiRvr_SampleUDP_Test(WifiRvrTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -779,6 +800,7 @@
 
 
 class WifiRvr_VHT_SampleUDP_Test(WifiRvrTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -789,6 +811,7 @@
 
 
 class WifiRvr_HE_SampleUDP_Test(WifiRvrTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -799,6 +822,7 @@
 
 
 class WifiRvr_SampleDFS_Test(WifiRvrTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -809,6 +833,7 @@
 
 
 class WifiRvr_SingleChain_TCP_Test(WifiRvrTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -867,6 +892,7 @@
     setting turntable orientation and other chamber parameters to study
     performance in varying channel conditions
     """
+
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
@@ -902,7 +928,12 @@
                 ]).items())
             if test_id not in plots:
                 # Initialize test id data when not present
-                compiled_data[test_id] = {'throughput': [], 'metrics': {}}
+                compiled_data[test_id] = {
+                    'throughput': [],
+                    'rx_phy_rate': [],
+                    'tx_phy_rate': [],
+                    'metrics': {}
+                }
                 compiled_data[test_id]['metrics'] = {
                     key: []
                     for key in result['metrics'].keys()
@@ -927,6 +958,8 @@
             # Compile test id data and metrics
             compiled_data[test_id]['throughput'].append(
                 result['throughput_receive'])
+            compiled_data[test_id]['rx_phy_rate'].append(result['rx_phy_rate'])
+            compiled_data[test_id]['tx_phy_rate'].append(result['tx_phy_rate'])
             compiled_data[test_id]['total_attenuation'] = result[
                 'total_attenuation']
             for metric_key, metric_value in result['metrics'].items():
@@ -935,18 +968,27 @@
             # Add test id to plots
             plots[test_id].add_line(result['total_attenuation'],
                                     result['throughput_receive'],
-                                    result['test_name'],
+                                    result['test_name'].strip('test_rvr_'),
                                     hover_text=result['hover_text'],
                                     width=1,
                                     style='dashed',
                                     marker='circle')
-            plots[test_id_phy].add_line(result['total_attenuation'],
-                                        result['avg_phy_rate'],
-                                        result['test_name'] + ' PHY',
-                                        hover_text=result['hover_text'],
-                                        width=1,
-                                        style='dashed',
-                                        marker='circle')
+            plots[test_id_phy].add_line(
+                result['total_attenuation'],
+                result['rx_phy_rate'],
+                result['test_name'].strip('test_rvr_') + ' Rx PHY Rate',
+                hover_text=result['hover_text'],
+                width=1,
+                style='dashed',
+                marker='inverted_triangle')
+            plots[test_id_phy].add_line(
+                result['total_attenuation'],
+                result['tx_phy_rate'],
+                result['test_name'].strip('test_rvr_') + ' Tx PHY Rate',
+                hover_text=result['hover_text'],
+                width=1,
+                style='dashed',
+                marker='triangle')
 
         # Compute average RvRs and compute metrics over orientations
         for test_id, test_data in compiled_data.items():
@@ -966,6 +1008,10 @@
                     metric_key, metric_value)
             test_data['avg_rvr'] = numpy.mean(test_data['throughput'], 0)
             test_data['median_rvr'] = numpy.median(test_data['throughput'], 0)
+            test_data['avg_rx_phy_rate'] = numpy.mean(test_data['rx_phy_rate'],
+                                                      0)
+            test_data['avg_tx_phy_rate'] = numpy.mean(test_data['tx_phy_rate'],
+                                                      0)
             plots[test_id].add_line(test_data['total_attenuation'],
                                     test_data['avg_rvr'],
                                     legend='Average Throughput',
@@ -974,6 +1020,15 @@
                                     test_data['median_rvr'],
                                     legend='Median Throughput',
                                     marker='square')
+            test_id_phy = test_id + tuple('PHY')
+            plots[test_id_phy].add_line(test_data['total_attenuation'],
+                                        test_data['avg_rx_phy_rate'],
+                                        legend='Average Rx Rate',
+                                        marker='inverted_triangle')
+            plots[test_id_phy].add_line(test_data['total_attenuation'],
+                                        test_data['avg_tx_phy_rate'],
+                                        legend='Average Tx Rate',
+                                        marker='triangle')
 
         figure_list = []
         for plot_id, plot in plots.items():
@@ -1019,6 +1074,7 @@
 
 
 class WifiOtaRvr_StandardOrientation_Test(WifiOtaRvrTest):
+
     def __init__(self, controllers):
         WifiOtaRvrTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(
@@ -1028,6 +1084,7 @@
 
 
 class WifiOtaRvr_SampleChannel_Test(WifiOtaRvrTest):
+
     def __init__(self, controllers):
         WifiOtaRvrTest.__init__(self, controllers)
         self.tests = self.generate_test_cases([6], ['bw20'],
@@ -1042,6 +1099,7 @@
 
 
 class WifiOtaRvr_SingleOrientation_Test(WifiOtaRvrTest):
+
     def __init__(self, controllers):
         WifiOtaRvrTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(
@@ -1050,6 +1108,7 @@
 
 
 class WifiOtaRvr_SingleChain_Test(WifiOtaRvrTest):
+
     def __init__(self, controllers):
         WifiOtaRvrTest.__init__(self, controllers)
         self.tests = self.generate_test_cases([6], ['bw20'],
diff --git a/acts_tests/tests/google/wifi/WifiRvrTwTest.py b/acts_tests/tests/google/wifi/WifiRvrTwTest.py
index e732b83..6e2babe 100644
--- a/acts_tests/tests/google/wifi/WifiRvrTwTest.py
+++ b/acts_tests/tests/google/wifi/WifiRvrTwTest.py
@@ -25,6 +25,8 @@
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts.controllers import iperf_server as ipf
+from acts.controllers import attenuator
+from acts.controllers.sl4a_lib import rpc_client
 
 import json
 import logging
@@ -35,446 +37,688 @@
 
 import serial
 import sys
+import urllib.request
 
+from acts_contrib.test_utils.wifi import wifi_performance_test_utils_RSSI as wperfutils
 
 WifiEnums = wutils.WifiEnums
 
 
-class WifiRvrTWTest(WifiBaseTest):
-    """ Tests for wifi RVR performance
+class WifiRvrTwTest(WifiBaseTest):
+  """ Tests for wifi RVR performance.
 
         Test Bed Requirement:
           * One Android device
           * Wi-Fi networks visible to the device
+  """
+  TEST_TIMEOUT = 10
+  IPERF_SETUP_TIME = 5
+  TURN_TABLE_SETUP_TIME = 5
+
+  def __init__(self, controllers):
+    WifiBaseTest.__init__(self, controllers)
+
+  def setup_class(self):
+    self.dut = self.android_devices[0]
+
+    req_params = ["rvr_networks", "rvr_test_params", "attenuators"]
+    opt_params = ["angle_params", "usb_port"]
+    self.unpack_userparams(
+        req_param_names=req_params, opt_param_names=opt_params)
+    asserts.assert_true(
+        len(self.rvr_networks) > 0, "Need at least one network.")
+
+    if "rvr_test_params" in self.user_params:
+      self.iperf_server = self.iperf_servers[0]
+      self.maxdb = self.rvr_test_params["rvr_atten_maxdb"]
+      self.mindb = self.rvr_test_params["rvr_atten_mindb"]
+      self.stepdb = self.rvr_test_params["rvr_atten_step"]
+      self.country_code = self.rvr_test_params["country_code"]
+    if "angle_params" in self.user_params:
+      self.angle_list = self.angle_params
+    if "usb_port" in self.user_params:
+      self.turntable_port = self.read_comport(self.usb_port["turntable"])
+
+    # Init DUT
+    wutils.wifi_test_device_init(self.dut, self.country_code)
+    self.dut.droid.bluetoothToggleState(False)
+    utils.set_location_service(self.dut, False)
+    wutils.wifi_toggle_state(self.dut, True)
+    utils.subprocess.check_output(
+        "adb root", shell=True, timeout=self.TEST_TIMEOUT)
+    utils.subprocess.check_output(
+        "adb shell settings put system screen_off_timeout 18000000",
+        shell=True,
+        timeout=self.TEST_TIMEOUT)
+    utils.subprocess.check_output(
+        "adb shell svc power stayon true",
+        shell=True,
+        timeout=self.TEST_TIMEOUT)
+
+    # create folder for rvr test result
+    self.log_path = os.path.join(logging.log_path, "rvr_results")
+    utils.create_dir(self.log_path)
+
+    Header = ("Test_date", "Project", "Device_SN", "ROM", "HW_Stage",
+              "test_SSID", "Frequency", "Turn_table_orientation",
+              "Attenuate_dB", "Signal_poll_avg_rssi", "Chain_0_rssi",
+              "Chain_1_rssi", "Link_speed", "TX_throughput_Mbps",
+              "RX_throughput_Mbps", "HE_Capable", "Country_code", "Channel",
+              "WiFi_chip", "Type", "Host_name", "AP_model",
+              "Incremental_build_id", "Build_type", "TCP_UDP_Protocol",
+              "Security_type", "Test_tool", "Airplane_mode_status", "BT_status",
+              "Bug_ID", "Comment")
+    self.csv_write(Header)
+
+  def setup_test(self):
+    self.dut.droid.wakeLockAcquireBright()
+    self.dut.droid.wakeUpNow()
+    rom_info = self.get_rominfo()
+    self.testdate = time.strftime("%Y-%m-%d", time.localtime())
+    self.rom = rom_info[0]
+    self.build_id = rom_info[1]
+    self.build_type = rom_info[2]
+    self.project = rom_info[3]
+    self.ret_country_code = self.get_country_code()
+    self.ret_hw_stage = self.get_hw_stage()
+    self.ret_platform = wperfutils.detect_wifi_platform(self.dut)
+
+  def teardown_test(self):
+    self.dut.droid.wakeLockRelease()
+    self.dut.droid.goToSleepNow()
+    wutils.set_attns(self.attenuators, "default")
+
+  def teardown_class(self):
+    if "rvr_test_params" in self.user_params:
+      self.iperf_server.stop()
+
+  def on_fail(self, test_name, begin_time):
+    self.dut.take_bug_report(test_name, begin_time)
+    self.dut.cat_adb_log(test_name, begin_time)
+
+  """Helper Functions"""
+
+  def csv_write(self, data):
+    """Output .CSV file for test result.
+
+    Args:
+        data: Dict containing attenuation, throughput and other meta data.
     """
-    TEST_TIMEOUT = 10
+    with open(
+        "{}/Result.csv".format(self.log_path), "a", newline="") as csv_file:
+      csv_writer = csv.writer(csv_file, delimiter=",")
+      csv_writer.writerow(data)
+      csv_file.close()
 
-    def setup_class(self):
-        super().setup_class()
+  def set_atten(self, db):
+    """Setup attenuator dB for current test.
 
-        self.dut = self.android_devices[0]
-        wutils.wifi_test_device_init(self.dut)
+    Args:
+       db: Attenuator setup dB.
+    """
+    if db < 0:
+      db = 0
+    elif db > 95:
+      db = 95
+    self.log.info("[Attenuation] %s", "Set dB = " + str(db) + "dB")
+    for atten in self.attenuators:
+      atten.set_atten(db)
+      self.log.info("[Attenuation] %s",
+                    "Current dB = " + str(atten.get_atten()) + "dB")
+      retry = 0
+      while atten.get_atten() != db and retry < 11:
+        retry = retry + 1
+        self.log.info(
+            "[Attenuation] %s", "Fail to set Attenuator to " + str(db) + ", " +
+            str(retry) + " times try to reset")
+        self.set_atten(db)
+      if retry == 11:
+        self.log.info("Attenuation] %s",
+                      "Retry Attenuator fail for 10 cycles, end test!")
+        sys.exit()
 
-        req_params = [ "iot_networks","rvr_test_params"]
-        opt_params = [ "angle_params","usb_port"]
-        self.unpack_userparams(req_param_names=req_params,
-                               opt_param_names=opt_params)
+  def read_comport(self, com):
+    """Read com port for current test.
 
-        asserts.assert_true(
-            len(self.iot_networks) > 0,
-            "Need at least one iot network with psk.")
+    Args:
+        com: Serial port.
 
-        wutils.wifi_toggle_state(self.dut, True)
-        if "rvr_test_params" in self.user_params:
-            self.iperf_server = self.iperf_servers[0]
-            self.MaxdB= self.rvr_test_params ["rvr_atten_MaxDB"]
-            self.MindB= self.rvr_test_params ["rvr_atten_MinDB"]
-            self.stepdB= self.rvr_test_params ["rvr_atten_step"]
+    Returns:
+        port: Serial port with baud rate.
+    """
+    port = serial.Serial(com, 9600, timeout=1)
+    time.sleep(1)
+    return port
 
-        if "angle_params" in self.user_params:
-            self.angle = self.angle_params
+  def get_angle(self, port):
+    """Get turn table angle for current test.
 
-        if "usb_port" in self.user_params:
-            self.T1=self.readport(self.usb_port["turntable"])
-            self.ATT1=self.readport(self.usb_port["atten1"])
-            self.ATT2=self.readport(self.usb_port["atten2"])
-            self.ATT3=self.readport(self.usb_port["atten3"])
+    Args:
+        port: Turn table com port.
 
-        # create hashmap for testcase name and SSIDs
-        self.iot_test_prefix = "test_iot_connection_to_"
-        self.ssid_map = {}
-        for network in self.iot_networks:
-            SSID = network['SSID'].replace('-','_')
-            self.ssid_map[SSID] = network
+    Returns:
+        angle: Angle from turn table.
+    """
+    angle = ""
+    port.write("DG?;".encode())
+    time.sleep(0.1)
+    degree_data = port.readline().decode("utf-8")
+    for data in range(len(degree_data)):
+      if (degree_data[data].isdigit()) is True:
+        angle = angle + degree_data[data]
+    if angle == "":
+      return -1
+    return int(angle)
 
-        # create folder for rvr test result
-        self.log_path = os.path.join(logging.log_path, "rvr_results")
-        os.makedirs(self.log_path, exist_ok=True)
+  def set_angle(self, port, angle):
+    """Setup turn table angle for current test.
 
-        Header=("test_SSID","Turn table (angle)","Attenuator(dBm)",
-                "TX throughput (Mbps)","RX throughput (Mbps)",
-                "RSSI","Link speed","Frequency")
-        self.csv_write(Header)
+    Args:
+        port: Turn table com port
+        angle: Turn table setup angle
+    """
+    if angle > 359:
+      angle = 359
+    elif angle < 0:
+      angle = 0
+    self.log.info("Set angle to " + str(angle))
+    input_angle = str("DG") + str(angle) + str(";")
+    port.write(input_angle.encode())
+    time.sleep(self.TURN_TABLE_SETUP_TIME)
 
-    def setup_test(self):
-        self.dut.droid.wakeLockAcquireBright()
-        self.dut.droid.wakeUpNow()
+  def check_angle(self, port, angle):
+    """Check turn table angle for current test.
 
-    def teardown_test(self):
-        self.dut.droid.wakeLockRelease()
-        self.dut.droid.goToSleepNow()
+    Args:
+        port: Turn table com port
+        angle: Turn table setup angle
+    """
+    retrytime = self.TEST_TIMEOUT
+    retry = 0
+    while self.get_angle(port) != angle and retry < retrytime:
+      retry = retry + 1
+      self.log.info("Turntable] %s",
+                    "Current angle = " + str(self.get_angle(port)))
+      self.log.info(
+          "Turntable] %s", "Fail set angle to " + str(angle) + ", " +
+          str(retry) + " times try to reset")
+      self.set_angle(port, angle)
+      time.sleep(self.TURN_TABLE_SETUP_TIME)
+    if retry == retrytime:
+      self.log.info(
+          "Turntable] %s",
+          "Retry turntable fail for " + str(retry) + " cycles, end test!")
+      sys.exit()
 
-    def teardown_class(self):
-        if "rvr_test_params" in self.user_params:
-            self.iperf_server.stop()
+  def get_wifiinfo(self):
+    """Get WiFi RSSI/ link speed/ frequency for current test.
 
-    def on_fail(self, test_name, begin_time):
-        self.dut.take_bug_report(test_name, begin_time)
-        self.dut.cat_adb_log(test_name, begin_time)
+    Returns:
+        [rssi,link_speed,frequency]: DUT WiFi RSSI,Link speed and Frequency.
+    """
+    def is_number(string):
+      for i in string:
+        if i.isdigit() is False:
+          if (i == "-" or i == "."):
+            continue
+          return str(-1)
+      return string
 
-    """Helper Functions"""
+    try:
+      cmd = "adb shell iw wlan0 link"
+      wifiinfo = utils.subprocess.check_output(
+          cmd, shell=True, timeout=self.TEST_TIMEOUT)
 
-    def csv_write(self,data):
-        """Output .CSV file for test result.
+      # Check RSSI Enhance
+      rssi = self.get_rssi_func()
 
-        Args:
-            data: Dict containing attenuation, throughput and other meta data.
-        """
-        with open("{}/Result.csv".format(self.log_path), "a", newline="") as csv_file:
-            csv_writer = csv.writer(csv_file,delimiter=',')
-            csv_writer.writerow(data)
-            csv_file.close()
+      # Check link speed
+      link_speed = wifiinfo.decode(
+          "utf-8")[wifiinfo.decode("utf-8").find("bitrate:") +
+                   8:wifiinfo.decode("utf-8").find("Bit/s") - 2]
+      link_speed = link_speed.strip(" ")
+      link_speed = is_number(link_speed)
+      # Check frequency
+      frequency = wifiinfo.decode(
+          "utf-8")[wifiinfo.decode("utf-8").find("freq:") +
+                   6:wifiinfo.decode("utf-8").find("freq:") + 10]
+      frequency = frequency.strip(" ")
+      frequency = is_number(frequency)
+    except:
+      return -1, -1, -1
+    return [rssi, link_speed, frequency]
 
-    def readport(self,com):
-        """Read com port for current test.
+  def get_rssi_func(self):
+    """Get RSSI from brcm/qcom wifi chip.
 
-        Args:
-            com: Attenuator or turn table com port
-        """
-        port=serial.Serial(com,9600,timeout=1)
-        time.sleep(1)
-        return port
+    Returns:
+         current_rssi: DUT WiFi RSSI.
+    """
+    if self.ret_platform == "brcm":
+      rssi_future = wperfutils.get_connected_rssi_brcm(self.dut)
+      signal_poll_avg_rssi_tmp = rssi_future.pop("signal_poll_avg_rssi").pop(
+          "mean")
+      chain_0_rssi_tmp = rssi_future.pop("chain_0_rssi").pop("mean")
+      chain_1_rssi_tmp = rssi_future.pop("chain_1_rssi").pop("mean")
+      current_rssi = {
+          "signal_poll_avg_rssi": signal_poll_avg_rssi_tmp,
+          "chain_0_rssi": chain_0_rssi_tmp,
+          "chain_1_rssi": chain_1_rssi_tmp
+      }
+    elif self.ret_platform == "qcom":
+      rssi_future = wperfutils.get_connected_rssi_qcom(
+          self.dut, interface="wlan0")
+      signal_poll_avg_rssi_tmp = rssi_future.pop("signal_poll_avg_rssi").pop(
+          "mean")
+      chain_0_rssi_tmp = rssi_future.pop("chain_0_rssi").pop("mean")
+      chain_1_rssi_tmp = rssi_future.pop("chain_1_rssi").pop("mean")
+      if math.isnan(signal_poll_avg_rssi_tmp):
+        signal_poll_avg_rssi_tmp = -1
+      if math.isnan(chain_0_rssi_tmp):
+        chain_0_rssi_tmp = -1
+      if math.isnan(chain_1_rssi_tmp):
+        chain_1_rssi_tmp = -1
 
-    def getdB(self,port):
-        """Get attenuator dB for current test.
+      if signal_poll_avg_rssi_tmp == -1 & chain_0_rssi_tmp == -1 & chain_1_rssi_tmp == -1:
+        current_rssi = -1
+      else:
+        current_rssi = {
+            "signal_poll_avg_rssi": signal_poll_avg_rssi_tmp,
+            "chain_0_rssi": chain_0_rssi_tmp,
+            "chain_1_rssi": chain_1_rssi_tmp
+        }
+    else:
+      current_rssi = {
+          "signal_poll_avg_rssi": float("nan"),
+          "chain_0_rssi": float("nan"),
+          "chain_1_rssi": float("nan")
+      }
+    return current_rssi
 
-        Args:
-            port: Attenuator com port
-        """
-        port.write('V?;'.encode())
-        dB=port.readline().decode()
-        dB=dB.strip(';')
-        dB=dB[dB.find('V')+1:]
-        return int(dB)
+  def get_rominfo(self):
+    """Get DUT ROM build info.
 
-    def setdB(self,port,dB):
-        """Setup attenuator dB for current test.
+    Returns:
+         rom, build_id, build_type, project: DUT Build info,Build ID,
+         Build type, and Project name
+    """
+    rom = "NA"
+    build_id = "NA"
+    build_type = "NA"
+    project = "NA"
+    rominfo = self.dut.adb.shell("getprop ro.build.display.id").split()
 
-        Args:
-            port: Attenuator com port
-            dB: Attenuator setup dB
-        """
-        if dB<0:
-            dB=0
-        elif dB>101:
-            dB=101
-        self.log.info("Set dB to "+str(dB))
-        InputdB=str('V')+str(dB)+str(';')
-        port.write(InputdB.encode())
-        time.sleep(0.1)
+    if rominfo:
+      rom = rominfo[2]
+      build_id = rominfo[3]
+      project, build_type = rominfo[0].split("-")
 
-    def set_Three_Att_dB(self,port1,port2,port3,dB):
-        """Setup 3 attenuator dB for current test.
+    return rom, build_id, build_type, project
 
-        Args:
-            port1: Attenuator1 com port
-            port1: Attenuator2 com port
-            port1: Attenuator com port
-            dB: Attenuator setup dB
-        """
-        self.setdB(port1,dB)
-        self.setdB(port2,dB)
-        self.setdB(port3,dB)
-        self.checkdB(port1,dB)
-        self.checkdB(port2,dB)
-        self.checkdB(port3,dB)
+  def get_hw_stage(self):
+    """Get DUT HW stage.
 
-    def checkdB(self,port,dB):
-        """Check attenuator dB for current test.
+    Returns:
+         hw_stage: DUT HW stage e.g. EVT/DVT/PVT..etc.
+    """
+    cmd = "adb shell getprop ro.boot.hardware.revision"
+    hw_stage_temp = utils.subprocess.check_output(
+        cmd, shell=True, timeout=self.TEST_TIMEOUT)
+    hw_stage = hw_stage_temp.decode("utf-8").split("\n")[0]
+    return hw_stage
 
-        Args:
-            port: Attenuator com port
-            dB: Attenuator setup dB
-        """
-        retry=0
-        while self.getdB(port)!=dB and retry<10:
-            retry=retry+1
-            self.log.info("Current dB = "+str(self.getdB(port)))
-            self.log.info("Fail to set Attenuator to "+str(dB)+", "
-                          +str(retry)+" times try to reset")
-            self.setdB(port,dB)
-        if retry ==10:
-            self.log.info("Retry Attenuator fail for 9 cycles, end test!")
-            sys.exit()
-        return 0
+  def get_country_code(self):
+    """Get DUT country code.
 
-    def getDG(self,port):
-        """Get turn table angle for current test.
+    Returns:
+         country_code: DUT country code e.g. US/JP/GE..etc.
+    """
+    cmd = "adb shell cmd wifi get-country-code"
+    country_code_temp = utils.subprocess.check_output(
+        cmd, shell=True, timeout=self.TEST_TIMEOUT)
+    country_code = country_code_temp.decode("utf-8").split(" ")[4].split(
+        "\n")[0]
+    return country_code
 
-        Args:
-            port: Turn table com port
-        """
-        DG = ""
-        port.write('DG?;'.encode())
-        time.sleep(0.1)
-        data = port.readline().decode('utf-8')
-        for i in range(len(data)):
-            if (data[i].isdigit()) == True:
-                DG = DG + data[i]
-        if DG == "":
-            return -1
-        return int(DG)
+  def get_channel(self):
+    """Get DUT WiFi channel.
 
-    def setDG(self,port,DG):
-        """Setup turn table angle for current test.
+    Returns:
+         country_code: DUT channel e.g. 6/36/37..etc.
+    """
+    if self.ret_platform == "brcm":
+      cmd = 'adb shell wl assoc | grep "Primary channel:"'
+      channel_temp = utils.subprocess.check_output(
+          cmd, shell=True, timeout=self.TEST_TIMEOUT)
+      channel = channel_temp.decode("utf-8").split(": ")[1].split("\n")[0]
+    elif self.ret_platform == "qcom":
+      cmd = "adb shell iw wlan0 info | grep channel"
+      channel_temp = utils.subprocess.check_output(
+          cmd, shell=True, timeout=self.TEST_TIMEOUT)
+      channel = channel_temp.decode("utf-8").split(" ")[1].split("\n")[0]
+    return channel
 
-        Args:
-            port: Turn table com port
-            DG: Turn table setup angle
-        """
-        if DG>359:
-            DG=359
-        elif DG<0:
-            DG=0
-        self.log.info("Set angle to "+str(DG))
-        InputDG=str('DG')+str(DG)+str(';')
-        port.write(InputDG.encode())
+  def get_he_capable(self):
+    """Get DUT WiFi high efficiency capable status .
 
-    def checkDG(self,port,DG):
-        """Check turn table angle for current test.
+    Returns:
+         he_capable: DUT high efficiency capable status.
+    """
+    if self.ret_platform == "brcm":
+      cmd = 'adb shell wl assoc | grep "Chanspec:"'
+      he_temp = utils.subprocess.check_output(
+          cmd, shell=True, timeout=self.TEST_TIMEOUT)
+      he_capable = he_temp.decode("utf-8").split(": ")[1].split("\n")[0].split(
+          "MHz")[0].split(" ")[3]
+    elif self.ret_platform == "qcom":
+      cmd = "adb shell iw wlan0 info | grep channel"
+      he_temp = utils.subprocess.check_output(
+          cmd, shell=True, timeout=self.TEST_TIMEOUT)
+      he_capable = he_temp.decode("utf-8").split("width: ")[1].split(" ")[0]
+    return he_capable
 
-        Args:
-            port: Turn table com port
-            DG: Turn table setup angle
-        """
-        retrytime = self.TEST_TIMEOUT
-        retry = 0
-        while self.getDG(port)!=DG and retry<retrytime:
-            retry=retry+1
-            self.log.info('Current angle = '+str(self.getDG(port)))
-            self.log.info('Fail set angle to '+str(DG)+', '+str(retry)+' times try to reset')
-            self.setDG(port,DG)
-            time.sleep(10)
-        if retry == retrytime:
-            self.log.info('Retry turntable fail for '+str(retry)+' cycles, end test!')
-            sys.exit()
-        return 0
+  def post_process_results(self, rvr_result):
+    """Saves JSON formatted results.
 
-    def getwifiinfo(self):
-        """Get WiFi RSSI/ link speed/ frequency for current test.
+    Args:
+        rvr_result: Dict containing attenuation, throughput and other meta data
+    Returns:
+        wifiinfo[0]: To check WiFi connection by RSSI value
+    """
+    # Save output as text file
+    wifiinfo = self.get_wifiinfo()
+    if wifiinfo[0] != -1:
+      rvr_result["signal_poll_avg_rssi"] = wifiinfo[0]["signal_poll_avg_rssi"]
+      rvr_result["chain_0_rssi"] = wifiinfo[0]["chain_0_rssi"]
+      rvr_result["chain_1_rssi"] = wifiinfo[0]["chain_1_rssi"]
+    else:
+      rvr_result["signal_poll_avg_rssi"] = wifiinfo[0]
+      rvr_result["chain_0_rssi"] = wifiinfo[0]
+      rvr_result["chain_1_rssi"] = wifiinfo[0]
+    if rvr_result["signal_poll_avg_rssi"] == -1:
+      rvr_result["channel"] = "NA"
+    else:
+      rvr_result["channel"] = self.ret_channel
+    rvr_result["country_code"] = self.ret_country_code
+    rvr_result["hw_stage"] = self.ret_hw_stage
+    rvr_result["wifi_chip"] = self.ret_platform
+    rvr_result["test_ssid"] = self.ssid
+    rvr_result["test_angle"] = self.angle_list[self.angle]
+    rvr_result["test_dB"] = self.db
+    rvr_result["test_link_speed"] = wifiinfo[1]
+    rvr_result["test_frequency"] = wifiinfo[2]
 
-        Returns:
-            [RSSI,LS,FR]: WiFi RSSI/ link speed/ frequency
-        """
-        def is_number(string):
-            for i in string:
-                if i.isdigit() == False:
-                    if (i=="-" or i=="."):
-                        continue
-                    return str(-1)
-            return string
+    data = (
+        self.testdate,
+        self.project,
+        self.dut.serial,
+        self.rom,
+        rvr_result["hw_stage"],
+        rvr_result["test_ssid"],
+        rvr_result["test_frequency"],
+        rvr_result["test_angle"],
+        rvr_result["test_dB"],
+        rvr_result["signal_poll_avg_rssi"],
+        rvr_result["chain_0_rssi"],
+        rvr_result["chain_1_rssi"],
+        rvr_result["test_link_speed"],
+        rvr_result["throughput_TX"][0],
+        rvr_result["throughput_RX"][0],
+        "HE" + self.he_capable,
+        rvr_result["country_code"],
+        rvr_result["channel"],
+        rvr_result["wifi_chip"],
+        "OTA_RvR",
+        "OTA_Testbed2",
+        "RAXE500",
+        self.build_id,
+        self.build_type,
+        "TCP",
+        "WPA3",
+        "iperf3",
+        "OFF",
+        "OFF",
+    )
+    self.csv_write(data)
 
-        try:
-            cmd = "adb shell iw wlan0 link"
-            wifiinfo = utils.subprocess.check_output(cmd,shell=True,
-                                                     timeout=self.TEST_TIMEOUT)
-            # Check RSSI
-            RSSI = wifiinfo.decode("utf-8")[wifiinfo.decode("utf-8").find("signal:") +
-                                            7:wifiinfo.decode("utf-8").find("dBm") - 1]
-            RSSI = RSSI.strip(' ')
-            RSSI = is_number(RSSI)
-            # Check link speed
-            LS = wifiinfo.decode("utf-8")[wifiinfo.decode("utf-8").find("bitrate:") +
-                                          8:wifiinfo.decode("utf-8").find("Bit/s") - 2]
-            LS = LS.strip(' ')
-            LS = is_number(LS)
-            # Check frequency
-            FR = wifiinfo.decode("utf-8")[wifiinfo.decode("utf-8").find("freq:") +
-                                          6:wifiinfo.decode("utf-8").find("freq:") + 10]
-            FR = FR.strip(' ')
-            FR = is_number(FR)
-        except:
-            return -1, -1, -1
-        return [RSSI,LS,FR]
+    results_file_path = "{}/{}_angle{}_{}dB.json".format(
+        self.log_path, self.ssid, self.angle_list[self.angle], self.db)
+    with open(results_file_path, "w") as results_file:
+      json.dump(rvr_result, results_file, indent=4)
+    return wifiinfo[0]
 
-    def post_process_results(self, rvr_result):
-        """Saves JSON formatted results.
+  def connect_to_wifi_network(self, network):
+    """Connection logic for wifi networks.
 
-        Args:
-            rvr_result: Dict containing attenuation, throughput and other meta
-            data
-        """
-        # Save output as text file
-        data=(rvr_result["test_name"],rvr_result["test_angle"],rvr_result["test_dB"],
-              rvr_result["throughput_TX"][0],rvr_result["throughput_RX"][0],
-              rvr_result["test_RSSI"],rvr_result["test_LS"],rvr_result["test_FR"])
-        self.csv_write(data)
+    Args:
+        params: Dictionary with network info.
+    """
+    ssid = network[WifiEnums.SSID_KEY]
+    self.dut.ed.clear_all_events()
+    wutils.start_wifi_connection_scan(self.dut)
+    scan_results = self.dut.droid.wifiGetScanResults()
+    wutils.assert_network_in_list({WifiEnums.SSID_KEY: ssid}, scan_results)
+    wutils.wifi_connect(self.dut, network, num_of_tries=3)
 
-        results_file_path = "{}/{}_angle{}_{}dB.json".format(self.log_path,
-                                                        self.ssid,
-                                                        self.angle[self.ag],self.DB)
-        with open(results_file_path, 'w') as results_file:
-            json.dump(rvr_result, results_file, indent=4)
+  def run_iperf_init(self, network):
+    self.iperf_server.start(tag="init")
+    self.log.info("[Iperf] %s", "Starting iperf traffic init.")
+    time.sleep(self.IPERF_SETUP_TIME)
+    try:
+      port_arg = "-p {} -J -R -t10".format(self.iperf_server.port)
+      self.dut.run_iperf_client(
+          self.rvr_test_params["iperf_server_address"],
+          port_arg,
+          timeout=self.rvr_test_params["iperf_duration"] + self.TEST_TIMEOUT)
+      self.iperf_server.stop()
+      self.log.info("[Iperf] %s", "iperf traffic init Pass")
+    except:
+      self.log.warning("ValueError: iperf init ERROR.")
 
-    def connect_to_wifi_network(self, network):
-        """Connection logic for psk wifi networks.
+  def run_iperf_client(self, network):
+    """Run iperf TX throughput after connection.
 
-        Args:
-            params: Dictionary with network info.
-        """
-        SSID = network[WifiEnums.SSID_KEY]
-        self.dut.ed.clear_all_events()
-        wutils.start_wifi_connection_scan(self.dut)
-        scan_results = self.dut.droid.wifiGetScanResults()
-        wutils.assert_network_in_list({WifiEnums.SSID_KEY: SSID}, scan_results)
-        wutils.wifi_connect(self.dut, network, num_of_tries=3)
+    Args:
+        network: Dictionary with network info.
 
-    def run_iperf_client(self, network):
-        """Run iperf TX throughput after connection.
+    Returns:
+        rvr_result: Dict containing TX rvr_results.
+    """
+    rvr_result = []
+    try:
+      self.iperf_server.start(tag="TX_server_{}_angle{}_{}dB".format(
+          self.ssid, self.angle_list[self.angle], self.db))
+      ssid = network[WifiEnums.SSID_KEY]
+      self.log.info("[Iperf] %s",
+                    "Starting iperf traffic TX through {}".format(ssid))
+      time.sleep(self.IPERF_SETUP_TIME)
+      port_arg = "-p {} -J {}".format(self.iperf_server.port,
+                                      self.rvr_test_params["iperf_port_arg"])
+      success, data = self.dut.run_iperf_client(
+          self.rvr_test_params["iperf_server_address"],
+          port_arg,
+          timeout=self.rvr_test_params["iperf_duration"] + self.TEST_TIMEOUT)
+      # Parse and log result
+      client_output_path = os.path.join(
+          self.iperf_server.log_path,
+          "IperfDUT,{},TX_client_{}_angle{}_{}dB".format(
+              self.iperf_server.port, self.ssid, self.angle_list[self.angle],
+              self.db))
+      with open(client_output_path, "w") as out_file:
+        out_file.write("\n".join(data))
+      self.iperf_server.stop()
 
-        Args:
-            params: Dictionary with network info.
+      iperf_file = self.iperf_server.log_files[-1]
+      iperf_result = ipf.IPerfResult(iperf_file)
+      curr_throughput = (math.fsum(iperf_result.instantaneous_rates[
+          self.rvr_test_params["iperf_ignored_interval"]:-1]) /
+                         len(iperf_result.instantaneous_rates[
+                             self.rvr_test_params["iperf_ignored_interval"]:-1])
+                        ) * 8 * (1.024**2)
+      rvr_result.append(curr_throughput)
+      self.log.info(
+          "[Iperf] %s", "TX Throughput at {0:.2f} dB is {1:.2f} Mbps".format(
+              self.db, curr_throughput))
+      self.log.debug(pprint.pformat(data))
+      asserts.assert_true(success, "Error occurred in iPerf traffic.")
+      return rvr_result
+    except:
+      rvr_result = ["NA"]
+      self.log.warning("ValueError: TX iperf ERROR.")
+      self.iperf_server.stop()
+      return rvr_result
 
-        Returns:
-            rvr_result: Dict containing rvr_results
-        """
-        rvr_result = []
-        self.iperf_server.start(tag="TX_server_{}_angle{}_{}dB".format(
-            self.ssid,self.angle[self.ag],self.DB))
-        wait_time = 5
-        SSID = network[WifiEnums.SSID_KEY]
-        self.log.info("Starting iperf traffic TX through {}".format(SSID))
-        time.sleep(wait_time)
-        port_arg = "-p {} -J {}".format(self.iperf_server.port,
-                                        self.rvr_test_params["iperf_port_arg"])
-        success, data = self.dut.run_iperf_client(
-            self.rvr_test_params["iperf_server_address"],
-            port_arg,
-            timeout=self.rvr_test_params["iperf_duration"] + self.TEST_TIMEOUT)
-        # Parse and log result
-        client_output_path = os.path.join(
-            self.iperf_server.log_path, "IperfDUT,{},TX_client_{}_angle{}_{}dB".format(
-                self.iperf_server.port,self.ssid,self.angle[self.ag],self.DB))
-        with open(client_output_path, 'w') as out_file:
-            out_file.write("\n".join(data))
-        self.iperf_server.stop()
+  def run_iperf_server(self, network):
+    """Run iperf RX throughput after connection.
 
-        iperf_file = self.iperf_server.log_files[-1]
-        try:
-            iperf_result = ipf.IPerfResult(iperf_file)
-            curr_throughput = (math.fsum(iperf_result.instantaneous_rates[
-                self.rvr_test_params["iperf_ignored_interval"]:-1]) / len(
-                    iperf_result.instantaneous_rates[self.rvr_test_params[
-                        "iperf_ignored_interval"]:-1])) * 8 * (1.024**2)
-        except:
-            self.log.warning(
-                "ValueError: Cannot get iperf result. Setting to 0")
-            curr_throughput = 0
-        rvr_result.append(curr_throughput)
-        self.log.info("TX Throughput at {0:.2f} dB is {1:.2f} Mbps".format(
-            self.DB, curr_throughput))
+    Args:
+        network: Dictionary with network info.
 
-        self.log.debug(pprint.pformat(data))
-        asserts.assert_true(success, "Error occurred in iPerf traffic.")
-        return rvr_result
+    Returns:
+        rvr_result: Dict containing RX rvr_results.
+    """
 
-    def run_iperf_server(self, network):
-        """Run iperf RX throughput after connection.
+    rvr_result = []
+    try:
+      self.iperf_server.start(tag="RX_client_{}_angle{}_{}dB".format(
+          self.ssid, self.angle_list[self.angle], self.db))
+      ssid = network[WifiEnums.SSID_KEY]
+      self.log.info("[Iperf] %s",
+                    "Starting iperf traffic RX through {}".format(ssid))
+      time.sleep(self.IPERF_SETUP_TIME)
+      port_arg = "-p {} -J -R {}".format(self.iperf_server.port,
+                                         self.rvr_test_params["iperf_port_arg"])
+      success, data = self.dut.run_iperf_client(
+          self.rvr_test_params["iperf_server_address"],
+          port_arg,
+          timeout=self.rvr_test_params["iperf_duration"] + self.TEST_TIMEOUT)
+      # Parse and log result
+      client_output_path = os.path.join(
+          self.iperf_server.log_path,
+          "IperfDUT,{},RX_server_{}_angle{}_{}dB".format(
+              self.iperf_server.port, self.ssid, self.angle_list[self.angle],
+              self.db))
+      with open(client_output_path, "w") as out_file:
+        out_file.write("\n".join(data))
+      self.iperf_server.stop()
 
-        Args:
-            params: Dictionary with network info.
+      iperf_file = client_output_path
+      iperf_result = ipf.IPerfResult(iperf_file)
+      curr_throughput = (math.fsum(iperf_result.instantaneous_rates[
+          self.rvr_test_params["iperf_ignored_interval"]:-1]) /
+                         len(iperf_result.instantaneous_rates[
+                             self.rvr_test_params["iperf_ignored_interval"]:-1])
+                        ) * 8 * (1.024**2)
+      rvr_result.append(curr_throughput)
+      self.log.info(
+          "[Iperf] %s", "RX Throughput at {0:.2f} dB is {1:.2f} Mbps".format(
+              self.db, curr_throughput))
 
-        Returns:
-            rvr_result: Dict containing rvr_results
-        """
-        rvr_result = []
-        self.iperf_server.start(tag="RX_client_{}_angle{}_{}dB".format(
-            self.ssid,self.angle[self.ag],self.DB))
-        wait_time = 5
-        SSID = network[WifiEnums.SSID_KEY]
-        self.log.info("Starting iperf traffic RX through {}".format(SSID))
-        time.sleep(wait_time)
-        port_arg = "-p {} -J -R {}".format(self.iperf_server.port,
-                                           self.rvr_test_params["iperf_port_arg"])
-        success, data = self.dut.run_iperf_client(
-            self.rvr_test_params["iperf_server_address"],
-            port_arg,
-            timeout=self.rvr_test_params["iperf_duration"] + self.TEST_TIMEOUT)
-        # Parse and log result
-        client_output_path = os.path.join(
-        self.iperf_server.log_path, "IperfDUT,{},RX_server_{}_angle{}_{}dB".format(
-            self.iperf_server.port,self.ssid,self.angle[self.ag],self.DB))
-        with open(client_output_path, 'w') as out_file:
-            out_file.write("\n".join(data))
-        self.iperf_server.stop()
+      self.log.debug(pprint.pformat(data))
+      asserts.assert_true(success, "Error occurred in iPerf traffic.")
+      return rvr_result
+    except:
+      rvr_result = ["NA"]
+      self.log.warning("ValueError: RX iperf ERROR.")
+      self.iperf_server.stop()
+      return rvr_result
 
-        iperf_file = client_output_path
-        try:
-            iperf_result = ipf.IPerfResult(iperf_file)
-            curr_throughput = (math.fsum(iperf_result.instantaneous_rates[
-                self.rvr_test_params["iperf_ignored_interval"]:-1]) / len(
-                    iperf_result.instantaneous_rates[self.rvr_test_params[
-                        "iperf_ignored_interval"]:-1])) * 8 * (1.024**2)
-        except:
-            self.log.warning(
-                "ValueError: Cannot get iperf result. Setting to 0")
-            curr_throughput = 0
-        rvr_result.append(curr_throughput)
-        self.log.info("RX Throughput at {0:.2f} dB is {1:.2f} Mbps".format(
-            self.DB, curr_throughput))
+  def iperf_test_func(self, network):
+    """Main function to test iperf TX/RX.
 
-        self.log.debug(pprint.pformat(data))
-        asserts.assert_true(success, "Error occurred in iPerf traffic.")
-        return rvr_result
+    Args:
+        network: Dictionary with network info.
+    """
+    # Initialize
+    rvr_result = {}
+    # Run RvR and log result
+    rvr_result["throughput_RX"] = self.run_iperf_server(network)
+    retry_time = 2
+    for retry in range(retry_time):
+      if rvr_result["throughput_RX"] == ["NA"]:
+        if not self.iperf_retry():
+          time.sleep(self.IPERF_SETUP_TIME)
+          rvr_result["throughput_RX"] = self.run_iperf_server(network)
+        else:
+          break
+      else:
+        break
+    rvr_result["throughput_TX"] = self.run_iperf_client(network)
+    retry_time = 2
+    for retry in range(retry_time):
+      if rvr_result["throughput_TX"] == ["NA"]:
+        if not self.iperf_retry():
+          time.sleep(self.IPERF_SETUP_TIME)
+          rvr_result["throughput_TX"] = self.run_iperf_client(network)
+        else:
+          break
+      else:
+        break
+    self.post_process_results(rvr_result)
+    self.rssi = wifiinfo[0]
+    return self.rssi
 
-    def iperf_test_func(self,network):
-        """Main function to test iperf TX/RX.
+  def iperf_retry(self):
+    """Check iperf TX/RX status and retry."""
+    try:
+      cmd = "adb -s {} shell pidof iperf3| xargs adb shell kill -9".format(
+          self.dut.serial)
+      utils.subprocess.call(cmd, shell=True, timeout=self.TEST_TIMEOUT)
+      self.log.warning("ValueError: Killed DUT iperf process, keep test")
+    except:
+      self.log.info("[Iperf] %s", "No iperf DUT process found, keep test")
 
-        Args:
-            params: Dictionary with network info
-        """
-        if "rvr_test_params" in self.user_params:
-            # Initialize
-            rvr_result = {}
-            # Run RvR and log result
-            wifiinfo = self.getwifiinfo()
-            rvr_result["throughput_TX"] = self.run_iperf_client(network)
-            rvr_result["throughput_RX"] = self.run_iperf_server(network)
-            rvr_result["test_name"] = self.ssid
-            rvr_result["test_angle"] = self.angle[self.ag]
-            rvr_result["test_dB"] = self.DB
-            rvr_result["test_RSSI"] = wifiinfo[0]
-            rvr_result["test_LS"] = wifiinfo[1]
-            rvr_result["test_FR"] = wifiinfo[2]
-            self.post_process_results(rvr_result)
+    wifiinfo = self.get_wifiinfo()
+    print("--[iperf_retry]--", wifiinfo[0])
+    self.log.info("[WiFiinfo] %s", "Current RSSI = " + str(wifiinfo[0]) + "dBm")
+    if wifiinfo[0] == -1:
+      self.log.warning("ValueError: Cannot get RSSI, stop throughput test")
+      return True
+    else:
+      return False
 
-    def rvr_test(self,network):
-        """Test function to run RvR.
+  def rvr_test(self, network):
+    """Test function to run RvR.
 
-        The function runs an RvR test in the current device/AP configuration.
-        Function is called from another wrapper function that sets up the
-        testbed for the RvR test
+    The function runs an RvR test in the current device/AP configuration.
+    Function is called from another wrapper function that sets up the
+    testbed for the RvR test
 
-        Args:
-            params: Dictionary with network info
-        """
-        wait_time = 5
-        utils.subprocess.check_output('adb root', shell=True, timeout=20)
-        self.ssid = network[WifiEnums.SSID_KEY]
-        self.log.info("Start rvr test")
-        for i in range(len(self.angle)):
-          self.setDG(self.T1,self.angle[i])
-          time.sleep(wait_time)
-          self.checkDG(self.T1,self.angle[i])
-          self.set_Three_Att_dB(self.ATT1,self.ATT2,self.ATT3,0)
-          time.sleep(wait_time)
-          self.connect_to_wifi_network(network)
-          self.set_Three_Att_dB(self.ATT1,self.ATT2,self.ATT3,self.MindB)
-          for j in range(self.MindB,self.MaxdB+self.stepdB,self.stepdB):
-            self.DB=j
-            self.ag=i
-            self.set_Three_Att_dB(self.ATT1,self.ATT2,self.ATT3,self.DB)
-            self.iperf_test_func(network)
-          wutils.reset_wifi(self.dut)
+    Args:
+        params: Dictionary with network info
+    """
+    self.ssid = network[WifiEnums.SSID_KEY]
+    self.log.info("Start rvr test")
 
-    """Tests"""
+    for angle in range(len(self.angle_list)):
+      self.angle = angle
+      self.set_angle(self.turntable_port, self.angle_list[angle])
+      self.check_angle(self.turntable_port, self.angle_list[angle])
+      self.set_atten(0)
+      self.connect_to_wifi_network(network)
+      self.ret_channel = self.get_channel()
+      self.he_capable = self.get_he_capable()
+      self.run_iperf_init(network)
+      for db in range(self.mindb, self.maxdb + self.stepdb, self.stepdb):
+        self.db = db
+        self.set_atten(self.db)
+        self.iperf_test_func(network)
+        if self.rssi == -1:
+          self.log.warning("ValueError: Cannot get RSSI. Run next angle")
+          break
+        else:
+          continue
+      wutils.reset_wifi(self.dut)
 
-    @test_tracker_info(uuid="93816af8-4c63-45f8-b296-cb49fae0b158")
-    def test_iot_connection_to_RVR_2G(self):
-        ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
-        self.rvr_test(self.ssid_map[ssid_key])
+  """Tests"""
+  def test_rvr_2g(self):
+    network = self.rvr_networks[0]
+    self.rvr_test(network)
 
-    @test_tracker_info(uuid="e1a67e13-946f-4d91-aa73-3f945438a1ac")
-    def test_iot_connection_to_RVR_5G(self):
-        ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
-        self.rvr_test(self.ssid_map[ssid_key])
\ No newline at end of file
+  def test_rvr_5g(self):
+    network = self.rvr_networks[1]
+    self.rvr_test(network)
+
+  def test_rvr_6g(self):
+    network = self.rvr_networks[2]
+    self.rvr_test(network)
diff --git a/acts_tests/tests/google/wifi/WifiSensitivityTest.py b/acts_tests/tests/google/wifi/WifiSensitivityTest.py
index 954bc90..535572d 100644
--- a/acts_tests/tests/google/wifi/WifiSensitivityTest.py
+++ b/acts_tests/tests/google/wifi/WifiSensitivityTest.py
@@ -375,18 +375,21 @@
             self.testbed_params['ap_tx_power_offset'][str(
                 testcase_params['channel'])] - ping_result['range'])
 
-    def setup_sensitivity_test(self, testcase_params):
-        # Setup test
-        if testcase_params['traffic_type'].lower() == 'ping':
-            self.setup_ping_test(testcase_params)
-            self.run_sensitivity_test = self.run_ping_test
-            self.process_sensitivity_test_results = (
-                self.process_ping_test_results)
-        else:
-            self.setup_rvr_test(testcase_params)
-            self.run_sensitivity_test = self.run_rvr_test
-            self.process_sensitivity_test_results = (
-                self.process_rvr_test_results)
+    def setup_ping_test(self, testcase_params):
+        """Function that gets devices ready for the test.
+
+        Args:
+            testcase_params: dict containing test-specific parameters
+        """
+        # Configure AP
+        self.setup_ap(testcase_params)
+        # Set attenuator to starting attenuation
+        for attenuator in self.attenuators:
+            attenuator.set_atten(testcase_params['atten_start'],
+                                 strict=False,
+                                 retry=True)
+        # Reset, configure, and connect DUT
+        self.setup_dut(testcase_params)
 
     def setup_ap(self, testcase_params):
         """Sets up the AP and attenuator to compensate for AP chain imbalance.
@@ -586,9 +589,14 @@
         ]
 
         # Prepare devices and run test
-        self.setup_sensitivity_test(testcase_params)
-        result = self.run_sensitivity_test(testcase_params)
-        self.process_sensitivity_test_results(testcase_params, result)
+        if testcase_params['traffic_type'].lower() == 'ping':
+            self.setup_ping_test(testcase_params)
+            result = self.run_ping_test(testcase_params)
+            self.process_ping_test_results(testcase_params, result)
+        else:
+            self.setup_rvr_test(testcase_params)
+            result = self.run_rvr_test(testcase_params)
+            self.process_rvr_test_results(testcase_params, result)
 
         # Post-process results
         self.testclass_results.append(result)
@@ -644,6 +652,7 @@
 
 
 class WifiSensitivity_AllChannels_Test(WifiSensitivityTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -652,6 +661,7 @@
 
 
 class WifiSensitivity_SampleChannels_Test(WifiSensitivityTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases([6, 36, 149],
@@ -660,6 +670,7 @@
 
 
 class WifiSensitivity_2GHz_Test(WifiSensitivityTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases([1, 2, 6, 10, 11], ['VHT20'],
@@ -667,6 +678,7 @@
 
 
 class WifiSensitivity_5GHz_Test(WifiSensitivityTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
@@ -675,6 +687,7 @@
 
 
 class WifiSensitivity_UNII1_Test(WifiSensitivityTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases([36, 40, 44, 48],
@@ -683,6 +696,7 @@
 
 
 class WifiSensitivity_UNII3_Test(WifiSensitivityTest):
+
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases([149, 153, 157, 161],
@@ -698,6 +712,7 @@
     It allows setting orientation and other chamber parameters to study
     performance in varying channel conditions
     """
+
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         self.testcase_metric_logger = (
@@ -911,6 +926,7 @@
 
 
 class WifiOtaSensitivity_TenDegree_Test(WifiOtaSensitivityTest):
+
     def __init__(self, controllers):
         WifiOtaSensitivityTest.__init__(self, controllers)
         requested_channels = [6, 36, 149]
@@ -929,6 +945,7 @@
 
 
 class WifiOtaSensitivity_PerChain_TenDegree_Test(WifiOtaSensitivityTest):
+
     def __init__(self, controllers):
         WifiOtaSensitivityTest.__init__(self, controllers)
         requested_channels = [6, 36, 149]
@@ -947,6 +964,7 @@
 
 
 class WifiOtaSensitivity_ThirtyDegree_Test(WifiOtaSensitivityTest):
+
     def __init__(self, controllers):
         WifiOtaSensitivityTest.__init__(self, controllers)
         requested_channels = [6, 36, 149]
@@ -971,6 +989,7 @@
 
 
 class WifiOtaSensitivity_45Degree_Test(WifiOtaSensitivityTest):
+
     def __init__(self, controllers):
         WifiOtaSensitivityTest.__init__(self, controllers)
         requested_rates = [
diff --git a/acts_tests/tests/google/wifi/WifiServiceApiTest.py b/acts_tests/tests/google/wifi/WifiServiceApiTest.py
index b656b7b..1e5e2af 100644
--- a/acts_tests/tests/google/wifi/WifiServiceApiTest.py
+++ b/acts_tests/tests/google/wifi/WifiServiceApiTest.py
@@ -14,10 +14,7 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import logging
 import queue
-import sys
-import time
 
 from acts import signals
 from acts import utils
@@ -36,7 +33,6 @@
            present).
     """
 
-
     TEST_SSID_PREFIX = "test_config_"
     CONFIG_ELEMENT = 'config'
     NETWORK_ID_ELEMENT = 'network_id'
@@ -61,7 +57,7 @@
         self.dut.droid.wifiEnableVerboseLogging(1)
         if self.dut.droid.wifiGetVerboseLoggingLevel() != 1:
             raise signals.TestFailure(
-                    "Failed to enable WiFi verbose logging on the dut.")
+                "Failed to enable WiFi verbose logging on the dut.")
 
     def teardown_class(self):
         wutils.reset_wifi(self.dut)
@@ -74,14 +70,18 @@
         """
         config_ssid = self.TEST_SSID_PREFIX + utils.rand_ascii_str(8)
         config_password = utils.rand_ascii_str(8)
-        self.dut.log.info("creating config: %s %s", config_ssid, config_password)
+        self.dut.log.info("creating config: %s %s", config_ssid,
+                          config_password)
         config = {wutils.WifiEnums.SSID_KEY: config_ssid}
         config[wutils.WifiEnums.PWD_KEY] = config_password
 
         # Now save the config.
         network_id = self.dut.droid.wifiAddNetwork(config)
         self.dut.log.info("saved config: network_id = %s", network_id)
-        return {self.NETWORK_ID_ELEMENT: network_id, self.CONFIG_ELEMENT: config}
+        return {
+            self.NETWORK_ID_ELEMENT: network_id,
+            self.CONFIG_ELEMENT: config
+        }
 
     def check_network_config_saved(self, config):
         """ Get the configured networks and check of the provided config
@@ -114,14 +114,15 @@
         self.dut.log.info("deleting config: networkId = %s", network_id)
         self.dut.droid.wifiForgetNetwork(network_id)
         try:
-            event = self.dut.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS, 10)
+            event = self.dut.ed.pop_event(
+                wifi_constants.WIFI_FORGET_NW_SUCCESS, 10)
             return True
         except queue.Empty:
             self.dut.log.error("Failed to forget network")
             return False
 
-
     """ Tests Begin """
+
     @test_tracker_info(uuid="f4df08c2-d3d5-4032-a433-c15f55130d4a")
     def test_remove_config_wifi_enabled(self):
         """ Test if config can be deleted when wifi is enabled.
@@ -134,15 +135,16 @@
         """
         wutils.wifi_toggle_state(self.dut, True)
         test_network = self.create_and_save_wifi_network_config()
-        if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+        if not self.check_network_config_saved(
+                test_network[self.CONFIG_ELEMENT]):
             raise signals.TestFailure(
-                    "Test network not found in list of configured networks.")
+                "Test network not found in list of configured networks.")
         if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
             raise signals.TestFailure(
-                    "Test network not deleted from configured networks.")
+                "Test network not deleted from configured networks.")
         if self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
             raise signals.TestFailure(
-                    "Deleted network was in configured networks list.")
+                "Deleted network was in configured networks list.")
 
     @test_tracker_info(uuid="9af96c7d-a316-4d57-ba5f-c992427c237b")
     def test_remove_config_wifi_disabled(self):
@@ -157,15 +159,16 @@
         """
         wutils.wifi_toggle_state(self.dut, True)
         test_network = self.create_and_save_wifi_network_config()
-        if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+        if not self.check_network_config_saved(
+                test_network[self.CONFIG_ELEMENT]):
             raise signals.TestFailure(
-                    "Test network not found in list of configured networks.")
+                "Test network not found in list of configured networks.")
         wutils.wifi_toggle_state(self.dut, False)
         if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
             raise signals.TestFailure("Failed to delete network.")
         if self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
             raise signals.TestFailure(
-                    "Test network was found in list of configured networks.")
+                "Test network was found in list of configured networks.")
 
     @test_tracker_info(uuid="79204ae6-323b-4257-a2cb-2225d44199d4")
     def test_retrieve_config_wifi_enabled(self):
@@ -179,9 +182,10 @@
         wutils.wifi_toggle_state(self.dut, True)
         test_network = self.create_and_save_wifi_network_config()
 
-        if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+        if not self.check_network_config_saved(
+                test_network[self.CONFIG_ELEMENT]):
             raise signals.TestFailure(
-                    "Test network not found in list of configured networks.")
+                "Test network not found in list of configured networks.")
         if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
             raise signals.TestFailure("Failed to delete network.")
 
@@ -196,9 +200,10 @@
         """
         wutils.wifi_toggle_state(self.dut, False)
         test_network = self.create_and_save_wifi_network_config()
-        if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+        if not self.check_network_config_saved(
+                test_network[self.CONFIG_ELEMENT]):
             raise signals.TestFailure(
-                    "Test network not found in list of configured networks.")
+                "Test network not found in list of configured networks.")
         if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
             raise signals.TestFailure("Failed to delete network.")
 
@@ -206,4 +211,4 @@
 
 
 if __name__ == "__main__":
-      pass
+    pass
diff --git a/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py b/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py
index 886754f..d1428b5 100644
--- a/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py
+++ b/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py
@@ -16,7 +16,6 @@
 
 import itertools
 import pprint
-import queue
 import sys
 import time
 
@@ -37,6 +36,7 @@
 WIFI_CONFIG_APBAND_AUTO = WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G
 GET_FREQUENCY_NUM_RETRIES = 3
 
+
 class WifiSoftApAcsTest(WifiBaseTest):
     """Tests for Automatic Channel Selection.
 
@@ -55,17 +55,26 @@
         utils.sync_device_time(self.dut_client)
         # Enable verbose logging on the duts
         self.dut.droid.wifiEnableVerboseLogging(1)
-        asserts.assert_equal(self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
+        asserts.assert_equal(
+            self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
             "Failed to enable WiFi verbose logging on the softap dut.")
         self.dut_client.droid.wifiEnableVerboseLogging(1)
-        asserts.assert_equal(self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
+        asserts.assert_equal(
+            self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
             "Failed to enable WiFi verbose logging on the client dut.")
-        req_params = ["wifi6_models",]
-        opt_param = ["iperf_server_address", "reference_networks",
-                     "iperf_server_port", "pixel_models"]
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_param)
-        self.chan_map = {v: k for k, v in hostapd_constants.CHANNEL_MAP.items()}
+        req_params = [
+            "wifi6_models",
+        ]
+        opt_param = [
+            "iperf_server_address", "reference_networks", "iperf_server_port",
+            "pixel_models"
+        ]
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_param)
+        self.chan_map = {
+            v: k
+            for k, v in hostapd_constants.CHANNEL_MAP.items()
+        }
         self.pcap_procs = None
 
     def setup_test(self):
@@ -75,8 +84,8 @@
             if chan.isnumeric():
                 band = '2G' if self.chan_map[int(chan)] < 5000 else '5G'
                 self.packet_capture[0].configure_monitor_mode(band, int(chan))
-                self.pcap_procs = wutils.start_pcap(
-                    self.packet_capture[0], band, self.test_name)
+                self.pcap_procs = wutils.start_pcap(self.packet_capture[0],
+                                                    band, self.test_name)
         self.dut.droid.wakeLockAcquireBright()
         self.dut.droid.wakeUpNow()
 
@@ -128,9 +137,10 @@
         config = wutils.create_softap_config()
         wutils.start_wifi_tethering(self.dut,
                                     config[wutils.WifiEnums.SSID_KEY],
-                                    config[wutils.WifiEnums.PWD_KEY], band=band)
+                                    config[wutils.WifiEnums.PWD_KEY],
+                                    band=band)
         asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
-                             "SoftAp is not reported as running")
+                            "SoftAp is not reported as running")
         wutils.start_wifi_connection_scan_and_ensure_network_found(
             self.dut_client, config[wutils.WifiEnums.SSID_KEY])
         return config
@@ -143,18 +153,19 @@
             softap: The softap network configuration information.
 
         """
-        wutils.connect_to_wifi_network(self.dut_client, softap,
-            check_connectivity=False)
+        wutils.connect_to_wifi_network(self.dut_client,
+                                       softap,
+                                       check_connectivity=False)
         for _ in range(GET_FREQUENCY_NUM_RETRIES):
             softap_info = self.dut_client.droid.wifiGetConnectionInfo()
             self.log.debug("DUT is connected to softAP %s with details: %s" %
                            (softap[wutils.WifiEnums.SSID_KEY], softap_info))
             frequency = softap_info['frequency']
-            self.dut.log.info("DUT SoftAp operates on Channel: {}".
-                              format(WifiEnums.freq_to_channel[frequency]))
+            self.dut.log.info("DUT SoftAp operates on Channel: {}".format(
+                WifiEnums.freq_to_channel[frequency]))
             if frequency > 0:
                 break
-            time.sleep(1) # frequency not updated yet, try again after a delay
+            time.sleep(1)  # frequency not updated yet, try again after a delay
         wutils.verify_11ax_softap(self.dut, self.dut_client, self.wifi6_models)
         return hostapd_constants.CHANNEL_MAP[frequency]
 
@@ -198,11 +209,12 @@
         wutils.connect_to_wifi_network(self.dut_client, network)
         freq = self.dut_client.droid.wifiGetConnectionInfo()["frequency"]
         ap_chan = wutils.WifiEnums.freq_to_channel[freq]
-        self.dut_client.log.info("{} operates on channel: {}"
-                                 .format(network["SSID"], ap_chan))
-        wutils.verify_11ax_wifi_connection(
-            self.dut_client, self.wifi6_models, "wifi6_ap" in self.user_params)
-        t = Thread(target=self.run_iperf_client,args=((network,self.dut_client),))
+        self.dut_client.log.info("{} operates on channel: {}".format(
+            network["SSID"], ap_chan))
+        wutils.verify_11ax_wifi_connection(self.dut_client, self.wifi6_models,
+                                           "wifi6_ap" in self.user_params)
+        t = Thread(target=self.run_iperf_client,
+                   args=((network, self.dut_client), ))
         t.setDaemon(True)
         t.start()
         time.sleep(1)
@@ -220,22 +232,25 @@
             avoid_chan: The channel to avoid during this test.
 
         """
-        if avoid_chan in range(1,12):
+        if avoid_chan in range(1, 12):
             avoid_chan2 = hostapd_constants.AP_DEFAULT_CHANNEL_5G
         elif avoid_chan in range(36, 166):
             avoid_chan2 = hostapd_constants.AP_DEFAULT_CHANNEL_2G
         if chan == avoid_chan or chan == avoid_chan2:
             raise signals.TestFailure("ACS chose the same channel that the "
-                "AP was beaconing on. Channel = %d" % chan)
+                                      "AP was beaconing on. Channel = %d" %
+                                      chan)
 
     """Tests"""
+
     @test_tracker_info(uuid="3507bd18-e787-4380-8725-1872916d4267")
     def test_softap_2G_clean_env(self):
         """Test to bring up SoftAp on 2GHz in clean environment."""
         network = None
         chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
         if not chan in range(1, 12):
-            raise signals.TestFailure("ACS chose incorrect channel %d for 2GHz "
+            raise signals.TestFailure(
+                "ACS chose incorrect channel %d for 2GHz "
                 "band" % chan)
 
     @test_tracker_info(uuid="3d18da8b-d29a-45f9-8018-5348e10099e9")
@@ -245,7 +260,8 @@
         chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
         if not chan in range(36, 166):
             # Note: This does not treat DFS channel separately.
-            raise signals.TestFailure("ACS chose incorrect channel %d for 5GHz "
+            raise signals.TestFailure(
+                "ACS chose incorrect channel %d for 5GHz "
                 "band" % chan)
 
     @test_tracker_info(uuid="cc353bda-3831-4d6e-b990-e501b8e4eafe")
@@ -255,7 +271,8 @@
         chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_AUTO)
         if not chan in range(36, 166):
             # Note: This does not treat DFS channel separately.
-            raise signals.TestFailure("ACS chose incorrect channel %d for 5GHz "
+            raise signals.TestFailure(
+                "ACS chose incorrect channel %d for 5GHz "
                 "band" % chan)
 
     @test_tracker_info(uuid="a5f6a926-76d2-46a7-8136-426e35b5a5a8")
diff --git a/acts_tests/tests/google/wifi/WifiSoftApMultiCountryTest.py b/acts_tests/tests/google/wifi/WifiSoftApMultiCountryTest.py
index f5173ff..4560573 100644
--- a/acts_tests/tests/google/wifi/WifiSoftApMultiCountryTest.py
+++ b/acts_tests/tests/google/wifi/WifiSoftApMultiCountryTest.py
@@ -14,11 +14,7 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-
-
 import logging
-import queue
-import random
 import time
 import re
 import acts.controllers.packet_capture as packet_capture
@@ -38,18 +34,17 @@
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
 from acts.controllers.ap_lib.hostapd_constants import CHANNEL_MAP
 
-
-
 WifiEnums = wutils.WifiEnums
 
-class WifiSoftApMultiCountryTest(WifiBaseTest):
 
+class WifiSoftApMultiCountryTest(WifiBaseTest):
     def __init__(self, configs):
         super().__init__(configs)
         self.basetest_name = (
-                "test_full_tether_startup_auto_one_client_ping_softap_multicountry",
-                "test_full_tether_startup_2G_one_client_ping_softap_multicountry",
-                "test_full_tether_startup_5G_one_client_ping_softap_multicountry",)
+            "test_full_tether_startup_auto_one_client_ping_softap_multicountry",
+            "test_full_tether_startup_2G_one_client_ping_softap_multicountry",
+            "test_full_tether_startup_5G_one_client_ping_softap_multicountry",
+        )
         self.generate_tests()
 
     def generate_testcase(self, basetest_name, country):
@@ -62,16 +57,15 @@
         base_test = getattr(self, basetest_name)
         test_tracker_uuid = ""
         testcase_name = 'test_%s_%s' % (basetest_name, country)
-        test_case = test_tracker_info(uuid=test_tracker_uuid)(
-            lambda: base_test(country))
+        test_case = test_tracker_info(
+            uuid=test_tracker_uuid)(lambda: base_test(country))
         setattr(self, testcase_name, test_case)
         self.tests.append(testcase_name)
 
     def generate_tests(self):
         for country in self.user_params['wifi_country_code']:
-                for basetest_name in self.basetest_name:
-                    self.generate_testcase(basetest_name, country)
-
+            for basetest_name in self.basetest_name:
+                self.generate_testcase(basetest_name, country)
 
     def setup_class(self):
         """It will setup the required dependencies from config file and configure
@@ -90,8 +84,8 @@
         self.channel_list_5g = WifiEnums.ALL_5G_FREQUENCIES
         req_params = ["dbs_supported_models"]
         opt_param = ["open_network"]
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_param)
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_param)
         if "AccessPoint" in self.user_params:
             self.legacy_configure_ap_and_start()
         elif "OpenWrtAP" in self.user_params:
@@ -107,10 +101,12 @@
         utils.sync_device_time(self.dut_client)
         # Enable verbose logging on the duts
         self.dut.droid.wifiEnableVerboseLogging(1)
-        asserts.assert_equal(self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
+        asserts.assert_equal(
+            self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
             "Failed to enable WiFi verbose logging on the softap dut.")
         self.dut_client.droid.wifiEnableVerboseLogging(1)
-        asserts.assert_equal(self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
+        asserts.assert_equal(
+            self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
             "Failed to enable WiFi verbose logging on the client dut.")
         wutils.wifi_toggle_state(self.dut, True)
         wutils.wifi_toggle_state(self.dut_client, True)
@@ -133,6 +129,7 @@
             wutils.stop_wifi_tethering(self.dut)
 
     """ Snifferconfig Functions """
+
     def conf_packet_capture(self, band, channel):
         """Configure packet capture on necessary channels."""
         freq_to_chan = wutils.WifiEnums.freq_to_channel[int(channel)]
@@ -142,12 +139,12 @@
         if not result:
             logging.error("Failed to configure channel "
                           "for {} band".format(band))
-        self.pcap_procs = wutils.start_pcap(
-            self.packet_capture, band, self.test_name)
+        self.pcap_procs = wutils.start_pcap(self.packet_capture, band,
+                                            self.test_name)
         time.sleep(5)
 
-
     """ Helper Functions """
+
     def create_softap_config(self):
         """Create a softap config with ssid and password."""
         ap_ssid = "softap_" + utils.rand_ascii_str(8)
@@ -169,11 +166,10 @@
         initial_wifi_state = self.dut.droid.wifiCheckState()
         self.dut.log.info("current state: %s", initial_wifi_state)
         config = self.create_softap_config()
-        wutils.start_wifi_tethering(
-                self.dut,
-                config[wutils.WifiEnums.SSID_KEY],
-                config[wutils.WifiEnums.PWD_KEY],
-                band=band)
+        wutils.start_wifi_tethering(self.dut,
+                                    config[wutils.WifiEnums.SSID_KEY],
+                                    config[wutils.WifiEnums.PWD_KEY],
+                                    band=band)
 
         if test_ping:
             self.validate_ping_between_softap_and_client(config)
@@ -185,7 +181,7 @@
             wutils.wait_for_wifi_state(self.dut, True)
         elif self.dut.droid.wifiCheckState():
             asserts.fail(
-                    "Wifi was disabled before softap and now it is enabled")
+                "Wifi was disabled before softap and now it is enabled")
 
     def validate_ping_between_softap_and_client(self, config):
         """Test ping between softap and its client.
@@ -210,16 +206,23 @@
         if hasattr(self, 'packet_capture'):
             self.conf_packet_capture(softap_band, softap_frequency)
         dut_ip = self.dut.droid.connectivityGetIPv4Addresses(self.AP_IFACE)[0]
-        dut_client_ip = self.dut_client.droid.connectivityGetIPv4Addresses('wlan0')[0]
+        dut_client_ip = self.dut_client.droid.connectivityGetIPv4Addresses(
+            'wlan0')[0]
 
         self.dut.log.info("Try to ping %s" % dut_client_ip)
         asserts.assert_true(
-            utils.adb_shell_ping(self.dut, count=10, dest_ip=dut_client_ip, timeout=20),
+            utils.adb_shell_ping(self.dut,
+                                 count=10,
+                                 dest_ip=dut_client_ip,
+                                 timeout=20),
             "%s ping %s failed" % (self.dut.serial, dut_client_ip))
 
         self.dut_client.log.info("Try to ping %s" % dut_ip)
         asserts.assert_true(
-            utils.adb_shell_ping(self.dut_client, count=10, dest_ip=dut_ip, timeout=20),
+            utils.adb_shell_ping(self.dut_client,
+                                 count=10,
+                                 dest_ip=dut_ip,
+                                 timeout=20),
             "%s ping %s failed" % (self.dut_client.serial, dut_ip))
 
         wutils.stop_wifi_tethering(self.dut)
@@ -234,7 +237,8 @@
     """ Tests Begin """
 
     @test_tracker_info(uuid="6ce4fb40-6fa7-452f-ba17-ea3fe47d325d")
-    def test_full_tether_startup_2G_one_client_ping_softap_multicountry(self, country):
+    def test_full_tether_startup_2G_one_client_ping_softap_multicountry(
+            self, country):
         """(AP) 1 Device can connect to 2G hotspot
 
         Steps:
@@ -245,13 +249,14 @@
         """
         wutils.set_wifi_country_code(self.dut, country)
         wutils.set_wifi_country_code(self.dut_client, country)
-        self.validate_full_tether_startup(WIFI_CONFIG_APBAND_2G, test_ping=True)
+        self.validate_full_tether_startup(WIFI_CONFIG_APBAND_2G,
+                                          test_ping=True)
         if hasattr(self, 'packet_capture'):
             wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
 
-
     @test_tracker_info(uuid="ae4629e6-08d5-4b51-ac34-6c2485f54df5")
-    def test_full_tether_startup_5G_one_client_ping_softap_multicountry(self, country):
+    def test_full_tether_startup_5G_one_client_ping_softap_multicountry(
+            self, country):
         """(AP) 1 Device can connect to 2G hotspot
 
         Steps:
@@ -262,13 +267,14 @@
         """
         wutils.set_wifi_country_code(self.dut, country)
         wutils.set_wifi_country_code(self.dut_client, country)
-        self.validate_full_tether_startup(WIFI_CONFIG_APBAND_5G, test_ping=True)
+        self.validate_full_tether_startup(WIFI_CONFIG_APBAND_5G,
+                                          test_ping=True)
         if hasattr(self, 'packet_capture'):
             wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
 
-
     @test_tracker_info(uuid="84a10203-cb02-433c-92a7-e8aa2348cc02")
-    def test_full_tether_startup_auto_one_client_ping_softap_multicountry(self, country):
+    def test_full_tether_startup_auto_one_client_ping_softap_multicountry(
+            self, country):
         """(AP) 1 Device can connect to hotspot
 
         Steps:
@@ -279,12 +285,11 @@
         """
         wutils.set_wifi_country_code(self.dut, country)
         wutils.set_wifi_country_code(self.dut_client, country)
-        self.validate_full_tether_startup(
-            WIFI_CONFIG_APBAND_AUTO, test_ping=True)
+        self.validate_full_tether_startup(WIFI_CONFIG_APBAND_AUTO,
+                                          test_ping=True)
         if hasattr(self, 'packet_capture'):
             wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
 
-
     """ Tests End """
 
 
diff --git a/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py b/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py
index 21333e3..d5c3ac5 100755
--- a/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py
+++ b/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py
@@ -15,7 +15,6 @@
 #   limitations under the License.
 
 import time
-import pprint
 import acts
 
 from acts import asserts
@@ -35,6 +34,7 @@
 WIFI_NETWORK_AP_CHANNEL_5G = 36
 WIFI_NETWORK_AP_CHANNEL_5G_DFS = 132
 
+
 class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest):
     """Stress tests for STA + AP concurrency scenarios.
 
@@ -45,21 +45,23 @@
 
     def __init__(self, controllers):
         WifiStaApConcurrencyTest.__init__(self, controllers)
-        self.tests = ("test_stress_wifi_connection_2G_softap_2G",
-                      "test_stress_wifi_connection_5G_softap_5G",
-                      "test_stress_wifi_connection_5G_DFS_softap_5G",
-                      "test_stress_wifi_connection_5G_softap_2G",
-                      "test_stress_wifi_connection_5G_DFS_softap_2G",
-                      "test_stress_wifi_connection_2G_softap_5G",
-                      "test_stress_wifi_connection_5G_softap_2G_with_location_scan_on",
-                      "test_stress_softap_2G_wifi_connection_2G",
-                      "test_stress_softap_5G_wifi_connection_5G",
-                      "test_stress_softap_5G_wifi_connection_5G_DFS",
-                      "test_stress_softap_5G_wifi_connection_2G",
-                      "test_stress_softap_2G_wifi_connection_5G",
-                      "test_stress_softap_2G_wifi_connection_5G_DFS",
-                      "test_stress_softap_5G_wifi_connection_2G_with_location_scan_on",
-                      "test_2g_sta_mode_and_hotspot_5g_on_off_stress_under_airplane_mode")
+        self.tests = (
+            "test_stress_wifi_connection_2G_softap_2G",
+            "test_stress_wifi_connection_5G_softap_5G",
+            "test_stress_wifi_connection_5G_DFS_softap_5G",
+            "test_stress_wifi_connection_5G_softap_2G",
+            "test_stress_wifi_connection_5G_DFS_softap_2G",
+            "test_stress_wifi_connection_2G_softap_5G",
+            "test_stress_wifi_connection_5G_softap_2G_with_location_scan_on",
+            "test_stress_softap_2G_wifi_connection_2G",
+            "test_stress_softap_5G_wifi_connection_5G",
+            "test_stress_softap_5G_wifi_connection_5G_DFS",
+            "test_stress_softap_5G_wifi_connection_2G",
+            "test_stress_softap_2G_wifi_connection_5G",
+            "test_stress_softap_2G_wifi_connection_5G_DFS",
+            "test_stress_softap_5G_wifi_connection_2G_with_location_scan_on",
+            "test_2g_sta_mode_and_hotspot_5g_on_off_stress_under_airplane_mode"
+        )
 
     def setup_class(self):
         super().setup_class()
@@ -88,6 +90,7 @@
             "Can not turn airplane mode off: %s" % self.dut.serial)
 
     """Helper Functions"""
+
     def connect_to_wifi_network_and_verify(self, params):
         """Connection logic for open and psk wifi networks.
         Args:
@@ -105,23 +108,26 @@
         wutils.wifi_toggle_state(self.dut, True)
         self.connect_to_wifi_network_and_verify((network, self.dut))
         if len(self.android_devices) > 2:
-            self.log.info("Testbed has extra android devices, do more validation")
-            self.verify_traffic_between_dut_clients(
-                    self.dut, self.android_devices[2])
+            self.log.info(
+                "Testbed has extra android devices, do more validation")
+            self.verify_traffic_between_dut_clients(self.dut,
+                                                    self.android_devices[2])
         wutils.wifi_toggle_state(self.dut, False)
 
     def verify_softap_full_on_off(self, network, softap_band):
         softap_config = self.start_softap_and_verify(softap_band)
         if len(self.android_devices) > 2:
-            self.log.info("Testbed has extra android devices, do more validation")
-            self.verify_traffic_between_dut_clients(
-                    self.dut_client, self.android_devices[2])
+            self.log.info(
+                "Testbed has extra android devices, do more validation")
+            self.verify_traffic_between_dut_clients(self.dut_client,
+                                                    self.android_devices[2])
         wutils.reset_wifi(self.dut_client)
         if len(self.android_devices) > 2:
             wutils.reset_wifi(self.android_devices[2])
         wutils.stop_wifi_tethering(self.dut)
 
     """Tests"""
+
     @test_tracker_info(uuid="615997cc-8290-4af3-b3ac-1f5bd5af6ed1")
     def test_stress_wifi_connection_2G_softap_2G(self):
         """Tests connection to 2G network the enable/disable SoftAp on 2G N times.
@@ -130,7 +136,7 @@
         wutils.wifi_toggle_state(self.dut, True)
         self.connect_to_wifi_network_and_verify((self.open_2g, self.dut))
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_softap_full_on_off(self.open_2g, WIFI_CONFIG_APBAND_2G)
 
     @test_tracker_info(uuid="03362d54-a624-4fb8-ad97-7abb9e6f655c")
@@ -141,7 +147,7 @@
         wutils.wifi_toggle_state(self.dut, True)
         self.connect_to_wifi_network_and_verify((self.open_5g, self.dut))
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_softap_full_on_off(self.open_5g, WIFI_CONFIG_APBAND_5G)
 
     @test_tracker_info(uuid="fdda4ff2-38d5-4398-9a59-c7cee407a2b3")
@@ -152,7 +158,7 @@
         wutils.wifi_toggle_state(self.dut, True)
         self.connect_to_wifi_network_and_verify((self.open_5g, self.dut))
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_softap_full_on_off(self.open_5g, WIFI_CONFIG_APBAND_5G)
 
     @test_tracker_info(uuid="b3621721-7714-43eb-8438-b578164b9194")
@@ -163,7 +169,7 @@
         wutils.wifi_toggle_state(self.dut, True)
         self.connect_to_wifi_network_and_verify((self.open_5g, self.dut))
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_softap_full_on_off(self.open_5g, WIFI_CONFIG_APBAND_2G)
 
     @test_tracker_info(uuid="bde1443f-f912-408e-b01a-537548dd023c")
@@ -184,7 +190,7 @@
         wutils.wifi_toggle_state(self.dut, True)
         self.connect_to_wifi_network_and_verify((self.open_2g, self.dut))
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_softap_full_on_off(self.open_2g, WIFI_CONFIG_APBAND_5G)
 
     @test_tracker_info(uuid="f28abf22-9df0-4500-b342-6682ca305e60")
@@ -197,7 +203,7 @@
         wutils.wifi_toggle_state(self.dut, True)
         self.connect_to_wifi_network_and_verify((self.open_5g, self.dut))
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_softap_full_on_off(self.open_5g, WIFI_CONFIG_APBAND_2G)
 
     @test_tracker_info(uuid="0edb1500-6c60-442e-9268-a2ad9ee2b55c")
@@ -205,10 +211,10 @@
         """Tests enable SoftAp on 2G then connection/disconnection to 2G network for N times.
         """
         self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
-        softap_config = self.start_softap_and_verify(
-                WIFI_CONFIG_APBAND_2G, check_connectivity=False)
+        softap_config = self.start_softap_and_verify(WIFI_CONFIG_APBAND_2G,
+                                                     check_connectivity=False)
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_wifi_full_on_off(self.open_2g, softap_config)
 
     @test_tracker_info(uuid="162a6679-edd5-4daa-9f25-75d79cf4bb4a")
@@ -216,10 +222,10 @@
         """Tests enable SoftAp on 5G then connection/disconnection to 5G network for N times.
         """
         self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
-        softap_config = self.start_softap_and_verify(
-                WIFI_CONFIG_APBAND_5G, check_connectivity=False)
+        softap_config = self.start_softap_and_verify(WIFI_CONFIG_APBAND_5G,
+                                                     check_connectivity=False)
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_wifi_full_on_off(self.open_5g, softap_config)
 
     @test_tracker_info(uuid="ee98f2dd-c4f9-4f48-ab59-f577267760d5")
@@ -227,10 +233,10 @@
         """Tests enable SoftAp on 5G then connection/disconnection to 5G DFS network for N times.
         """
         self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
-        softap_config = self.start_softap_and_verify(
-                WIFI_CONFIG_APBAND_5G, check_connectivity=False)
+        softap_config = self.start_softap_and_verify(WIFI_CONFIG_APBAND_5G,
+                                                     check_connectivity=False)
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_wifi_full_on_off(self.open_5g, softap_config)
 
     @test_tracker_info(uuid="b50750b5-d5b9-4687-b9e7-9fb15f54b428")
@@ -238,10 +244,10 @@
         """Tests enable SoftAp on 5G then connection/disconnection to 2G network for N times.
         """
         self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
-        softap_config = self.start_softap_and_verify(
-                WIFI_CONFIG_APBAND_5G, check_connectivity=False)
+        softap_config = self.start_softap_and_verify(WIFI_CONFIG_APBAND_5G,
+                                                     check_connectivity=False)
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_wifi_full_on_off(self.open_2g, softap_config)
 
     @test_tracker_info(uuid="9a2865db-8e4b-4339-9999-000ce9b6970b")
@@ -249,10 +255,10 @@
         """Tests enable SoftAp on 2G then connection/disconnection to 5G network for N times.
         """
         self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
-        softap_config = self.start_softap_and_verify(
-                WIFI_CONFIG_APBAND_2G, check_connectivity=False)
+        softap_config = self.start_softap_and_verify(WIFI_CONFIG_APBAND_2G,
+                                                     check_connectivity=False)
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_wifi_full_on_off(self.open_5g, softap_config)
 
     @test_tracker_info(uuid="add6609d-91d6-4b89-94c5-0ad8b941e3d1")
@@ -260,10 +266,10 @@
         """Tests enable SoftAp on 2G then connection/disconnection to 5G DFS network for N times.
         """
         self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
-        softap_config = self.start_softap_and_verify(
-                WIFI_CONFIG_APBAND_2G, check_connectivity=False)
+        softap_config = self.start_softap_and_verify(WIFI_CONFIG_APBAND_2G,
+                                                     check_connectivity=False)
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_wifi_full_on_off(self.open_5g, softap_config)
 
     @test_tracker_info(uuid="ee42afb6-99d0-4330-933f-d4dd8c3626c6")
@@ -273,14 +279,15 @@
         """
         self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
         self.turn_location_on_and_scan_toggle_on()
-        softap_config = self.start_softap_and_verify(
-                WIFI_CONFIG_APBAND_5G, check_connectivity=False)
+        softap_config = self.start_softap_and_verify(WIFI_CONFIG_APBAND_5G,
+                                                     check_connectivity=False)
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
+            self.log.info("Iteration %d", count + 1)
             self.verify_wifi_full_on_off(self.open_2g, softap_config)
 
     @test_tracker_info(uuid="36c7f847-4b3e-4bb1-a280-cfe2b6afc903")
-    def test_2g_sta_mode_and_hotspot_5g_on_off_stress_under_airplane_mode(self):
+    def test_2g_sta_mode_and_hotspot_5g_on_off_stress_under_airplane_mode(
+            self):
         """Tests connection to 2G network followed by bringing up SoftAp on 5G
         under airplane mode
         """
@@ -294,5 +301,5 @@
         self.connect_to_wifi_network_and_verify((self.open_2g, self.dut))
         time.sleep(DEFAULT_TIMEOUT)
         for count in range(self.stress_count):
-            self.log.info("Iteration %d", count+1)
-            self.verify_softap_full_on_off(self.open_2g, WIFI_CONFIG_APBAND_5G)
\ No newline at end of file
+            self.log.info("Iteration %d", count + 1)
+            self.verify_softap_full_on_off(self.open_2g, WIFI_CONFIG_APBAND_5G)
diff --git a/acts_tests/tests/google/wifi/WifiStressTest.py b/acts_tests/tests/google/wifi/WifiStressTest.py
index da88221..581a7ff 100644
--- a/acts_tests/tests/google/wifi/WifiStressTest.py
+++ b/acts_tests/tests/google/wifi/WifiStressTest.py
@@ -14,7 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import pprint
 import queue
 import threading
 import time
@@ -30,6 +29,7 @@
 from acts_contrib.test_utils.bt.bt_test_utils import enable_bluetooth
 from acts_contrib.test_utils.bt.bt_test_utils import disable_bluetooth
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
 WifiEnums = wutils.WifiEnums
 
 WAIT_FOR_AUTO_CONNECT = 40
@@ -39,6 +39,7 @@
 BAND_2GHZ = 0
 BAND_5GHZ = 1
 
+
 class WifiStressTest(WifiBaseTest):
     """WiFi Stress test class.
 
@@ -47,6 +48,7 @@
     * Several Wi-Fi networks visible to the device, including an open Wi-Fi
       network.
     """
+
     def __init__(self, configs):
         super().__init__(configs)
         self.enable_packet_log = True
@@ -66,9 +68,10 @@
         opt_param = [
             "open_network", "reference_networks", "iperf_server_address",
             "stress_count", "stress_hours", "attn_vals", "pno_interval",
-            "iperf_server_port"]
-        self.unpack_userparams(
-            req_param_names=req_params, opt_param_names=opt_param)
+            "iperf_server_port"
+        ]
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_param)
 
         if "AccessPoint" in self.user_params:
             self.legacy_configure_ap_and_start(ap_count=2)
@@ -129,8 +132,8 @@
 
         """
         ssid = network[WifiEnums.SSID_KEY]
-        wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut,
-            ssid)
+        wutils.start_wifi_connection_scan_and_ensure_network_found(
+            self.dut, ssid)
         wutils.wifi_connect_by_id(self.dut, net_id)
 
     def run_ping(self, sec):
@@ -141,8 +144,8 @@
 
         """
         self.log.info("Running ping for %d seconds" % sec)
-        result = self.dut.adb.shell("ping -w %d %s" %(sec, PING_ADDR),
-            timeout=sec+1)
+        result = self.dut.adb.shell("ping -w %d %s" % (sec, PING_ADDR),
+                                    timeout=sec + 1)
         self.log.debug("Ping Result = %s" % result)
         if "100% packet loss" in result:
             raise signals.TestFailure("100% packet loss during ping")
@@ -161,7 +164,8 @@
         video_played = False
         for count in range(2):
             ad.unlock_screen()
-            ad.adb.shell('am start -a android.intent.action.VIEW -d "%s"' % url)
+            ad.adb.shell('am start -a android.intent.action.VIEW -d "%s"' %
+                         url)
             if tutils.wait_for_state(ad.droid.audioIsMusicActive, True, 15, 1):
                 ad.log.info("Started a video in youtube.")
                 # Play video for given seconds.
@@ -169,7 +173,8 @@
                 video_played = True
                 break
         if not video_played:
-            raise signals.TestFailure("Youtube video did not start. Current WiFi "
+            raise signals.TestFailure(
+                "Youtube video did not start. Current WiFi "
                 "state is %d" % self.dut.droid.wifiCheckState())
 
     def add_networks(self, ad, networks):
@@ -182,8 +187,8 @@
         """
         for network in networks:
             ret = ad.droid.wifiAddNetwork(network)
-            asserts.assert_true(ret != -1, "Failed to add network %s" %
-                                network)
+            asserts.assert_true(ret != -1,
+                                "Failed to add network %s" % network)
             ad.droid.wifiEnableNetwork(ret, 0)
         configured_networks = ad.droid.wifiGetConfiguredNetworks()
         self.log.debug("Configured networks: %s", configured_networks)
@@ -210,13 +215,14 @@
             self.log.info("Wait 60s for network selection.")
             time.sleep(60)
         try:
-            self.log.info("Connected to %s network after network selection"
-                          % self.dut.droid.wifiGetConnectionInfo())
+            self.log.info("Connected to %s network after network selection" %
+                          self.dut.droid.wifiGetConnectionInfo())
             expected_ssid = expected_con[WifiEnums.SSID_KEY]
             verify_con = {WifiEnums.SSID_KEY: expected_ssid}
             wutils.verify_wifi_connection_info(self.dut, verify_con)
-            self.log.info("Connected to %s successfully after network selection",
-                          expected_ssid)
+            self.log.info(
+                "Connected to %s successfully after network selection",
+                expected_ssid)
         finally:
             pass
 
@@ -225,7 +231,8 @@
             # Start IPerf traffic
             self.log.info("Running iperf client {}".format(args))
             result, data = self.dut.run_iperf_client(self.iperf_server_address,
-                args, timeout=sec+1)
+                                                     args,
+                                                     timeout=sec + 1)
             if not result:
                 self.log.debug("Error occurred in iPerf traffic.")
                 self.run_ping(sec)
@@ -248,12 +255,18 @@
                 wutils.wifi_toggle_state(self.dut, True)
                 startup_time = time.time() - startTime
                 self.log.debug("WiFi was enabled on the device in %s s." %
-                    startup_time)
+                               startup_time)
             except:
-                signals.TestFailure(details="", extras={"Iterations":"%d" %
-                    self.stress_count, "Pass":"%d" %count})
-        raise signals.TestPass(details="", extras={"Iterations":"%d" %
-            self.stress_count, "Pass":"%d" %(count+1)})
+                signals.TestFailure(details="",
+                                    extras={
+                                        "Iterations": "%d" % self.stress_count,
+                                        "Pass": "%d" % count
+                                    })
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % (count + 1)
+                               })
 
     @test_tracker_info(uuid="4e591cec-9251-4d52-bc6e-6621507524dc")
     def test_stress_toggle_wifi_state_bluetooth_on(self):
@@ -269,13 +282,19 @@
                 wutils.wifi_toggle_state(self.dut, True)
                 startup_time = time.time() - startTime
                 self.log.debug("WiFi was enabled on the device in %s s." %
-                    startup_time)
+                               startup_time)
             except:
-                signals.TestFailure(details="", extras={"Iterations":"%d" %
-                    self.stress_count, "Pass":"%d" %count})
+                signals.TestFailure(details="",
+                                    extras={
+                                        "Iterations": "%d" % self.stress_count,
+                                        "Pass": "%d" % count
+                                    })
         disable_bluetooth(self.dut.droid)
-        raise signals.TestPass(details="", extras={"Iterations":"%d" %
-            self.stress_count, "Pass":"%d" %(count+1)})
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % (count + 1)
+                               })
 
     @test_tracker_info(uuid="49e3916a-9580-4bf7-a60d-a0f2545dcdde")
     def test_stress_connect_traffic_disconnect_5g(self):
@@ -291,24 +310,34 @@
         for count in range(self.stress_count):
             try:
                 net_id = self.dut.droid.wifiAddNetwork(self.wpa_5g)
-                asserts.assert_true(net_id != -1, "Add network %r failed" % self.wpa_5g)
+                asserts.assert_true(net_id != -1,
+                                    "Add network %r failed" % self.wpa_5g)
                 self.scan_and_connect_by_id(self.wpa_5g, net_id)
                 # Start IPerf traffic from phone to server.
                 # Upload data for 10s.
                 args = "-p {} -t {}".format(self.iperf_server_port, 10)
                 self.log.info("Running iperf client {}".format(args))
-                result, data = self.dut.run_iperf_client(self.iperf_server_address, args)
+                result, data = self.dut.run_iperf_client(
+                    self.iperf_server_address, args)
                 if not result:
                     self.log.debug("Error occurred in iPerf traffic.")
                     self.run_ping(10)
-                wutils.wifi_forget_network(self.dut,self.wpa_5g[WifiEnums.SSID_KEY])
+                wutils.wifi_forget_network(self.dut,
+                                           self.wpa_5g[WifiEnums.SSID_KEY])
                 time.sleep(WAIT_BEFORE_CONNECTION)
             except:
-                raise signals.TestFailure("Network connect-disconnect failed."
-                    "Look at logs", extras={"Iterations":"%d" %
-                        self.stress_count, "Pass":"%d" %count})
-        raise signals.TestPass(details="", extras={"Iterations":"%d" %
-            self.stress_count, "Pass":"%d" %(count+1)})
+                raise signals.TestFailure(
+                    "Network connect-disconnect failed."
+                    "Look at logs",
+                    extras={
+                        "Iterations": "%d" % self.stress_count,
+                        "Pass": "%d" % count
+                    })
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % (count + 1)
+                               })
 
     @test_tracker_info(uuid="e9827dff-0755-43ec-8b50-1f9756958460")
     def test_stress_connect_long_traffic_5g(self):
@@ -329,19 +358,26 @@
         start_time = time.time()
 
         dl_args = "-p {} -t {} -b1M -R".format(self.iperf_server_port, sec)
-        dl = threading.Thread(target=self.run_long_traffic, args=(sec, dl_args, q))
+        dl = threading.Thread(target=self.run_long_traffic,
+                              args=(sec, dl_args, q))
         dl.start()
         dl.join()
 
         total_time = time.time() - start_time
-        self.log.debug("WiFi state = %d" %self.dut.droid.wifiCheckState())
-        while(q.qsize() > 0):
+        self.log.debug("WiFi state = %d" % self.dut.droid.wifiCheckState())
+        while (q.qsize() > 0):
             if not q.get():
                 raise signals.TestFailure("Network long-connect failed.",
-                    extras={"Total Hours":"%d" %self.stress_hours,
-                    "Seconds Run":"%d" %total_time})
-        raise signals.TestPass(details="", extras={"Total Hours":"%d" %
-            self.stress_hours, "Seconds Run":"%d" %total_time})
+                                          extras={
+                                              "Total Hours":
+                                              "%d" % self.stress_hours,
+                                              "Seconds Run": "%d" % total_time
+                                          })
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Total Hours": "%d" % self.stress_hours,
+                                   "Seconds Run": "%d" % total_time
+                               })
 
     @test_tracker_info(uuid="591d257d-9477-4a89-a220-5715c93a76a7")
     def test_stress_youtube_5g(self):
@@ -354,26 +390,35 @@
 
         """
         # List of Youtube 4K videos.
-        videos = ["https://www.youtube.com/watch?v=TKmGU77INaM",
-                  "https://www.youtube.com/watch?v=WNCl-69POro",
-                  "https://www.youtube.com/watch?v=dVkK36KOcqs",
-                  "https://www.youtube.com/watch?v=0wCC3aLXdOw",
-                  "https://www.youtube.com/watch?v=rN6nlNC9WQA",
-                  "https://www.youtube.com/watch?v=RK1K2bCg4J8"]
+        videos = [
+            "https://www.youtube.com/watch?v=TKmGU77INaM",
+            "https://www.youtube.com/watch?v=WNCl-69POro",
+            "https://www.youtube.com/watch?v=dVkK36KOcqs",
+            "https://www.youtube.com/watch?v=0wCC3aLXdOw",
+            "https://www.youtube.com/watch?v=rN6nlNC9WQA",
+            "https://www.youtube.com/watch?v=RK1K2bCg4J8"
+        ]
         try:
             self.scan_and_connect_by_ssid(self.dut, self.wpa_5g)
             start_time = time.time()
             for video in videos:
-                self.start_youtube_video(url=video, secs=10*60)
+                self.start_youtube_video(url=video, secs=10 * 60)
         except:
             total_time = time.time() - start_time
             raise signals.TestFailure("The youtube stress test has failed."
-                "WiFi State = %d" %self.dut.droid.wifiCheckState(),
-                extras={"Total Hours":"1", "Seconds Run":"%d" %total_time})
+                                      "WiFi State = %d" %
+                                      self.dut.droid.wifiCheckState(),
+                                      extras={
+                                          "Total Hours": "1",
+                                          "Seconds Run": "%d" % total_time
+                                      })
         total_time = time.time() - start_time
-        self.log.debug("WiFi state = %d" %self.dut.droid.wifiCheckState())
-        raise signals.TestPass(details="", extras={"Total Hours":"1",
-            "Seconds Run":"%d" %total_time})
+        self.log.debug("WiFi state = %d" % self.dut.droid.wifiCheckState())
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Total Hours": "1",
+                                   "Seconds Run": "%d" % total_time
+                               })
 
     @test_tracker_info(uuid="d367c83e-5b00-4028-9ed8-f7b875997d13")
     def test_stress_wifi_failover(self):
@@ -388,20 +433,21 @@
                   exhausted.
 
         """
-        for count in range(int(self.stress_count/4)):
+        for count in range(int(self.stress_count / 4)):
             wutils.reset_wifi(self.dut)
             ssids = list()
             for network in self.networks:
                 ssids.append(network[WifiEnums.SSID_KEY])
                 ret = self.dut.droid.wifiAddNetwork(network)
-                asserts.assert_true(ret != -1, "Add network %r failed" % network)
+                asserts.assert_true(ret != -1,
+                                    "Add network %r failed" % network)
                 self.dut.droid.wifiEnableNetwork(ret, 0)
             self.dut.droid.wifiStartScan()
             time.sleep(WAIT_FOR_AUTO_CONNECT)
             cur_network = self.dut.droid.wifiGetConnectionInfo()
             cur_ssid = cur_network[WifiEnums.SSID_KEY]
             self.log.info("Cur_ssid = %s" % cur_ssid)
-            for i in range(0,len(self.networks)):
+            for i in range(0, len(self.networks)):
                 self.log.debug("Forget network %s" % cur_ssid)
                 wutils.wifi_forget_network(self.dut, cur_ssid)
                 time.sleep(WAIT_FOR_AUTO_CONNECT)
@@ -412,16 +458,23 @@
                     break
                 if cur_ssid not in ssids:
                     raise signals.TestFailure("Device did not failover to the "
-                        "expected network. SSID = %s" % cur_ssid)
+                                              "expected network. SSID = %s" %
+                                              cur_ssid)
             network_config = self.dut.droid.wifiGetConfiguredNetworks()
             self.log.info("Network Config = %s" % network_config)
             if len(network_config):
-                raise signals.TestFailure("All the network configurations were not "
+                raise signals.TestFailure(
+                    "All the network configurations were not "
                     "removed. Configured networks = %s" % network_config,
-                        extras={"Iterations":"%d" % self.stress_count,
-                            "Pass":"%d" %(count*4)})
-        raise signals.TestPass(details="", extras={"Iterations":"%d" %
-            self.stress_count, "Pass":"%d" %((count+1)*4)})
+                    extras={
+                        "Iterations": "%d" % self.stress_count,
+                        "Pass": "%d" % (count * 4)
+                    })
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % ((count + 1) * 4)
+                               })
 
     @test_tracker_info(uuid="2c19e8d1-ac16-4d7e-b309-795144e6b956")
     def test_stress_softAP_startup_and_stop_5g(self):
@@ -441,13 +494,12 @@
         config[wutils.WifiEnums.PWD_KEY] = ap_password
         # Set country code explicitly to "US".
         wutils.set_wifi_country_code(self.dut, wutils.WifiEnums.CountryCode.US)
-        wutils.set_wifi_country_code(self.dut_client, wutils.WifiEnums.CountryCode.US)
+        wutils.set_wifi_country_code(self.dut_client,
+                                     wutils.WifiEnums.CountryCode.US)
         for count in range(self.stress_count):
             initial_wifi_state = self.dut.droid.wifiCheckState()
-            wutils.start_wifi_tethering(self.dut,
-                ap_ssid,
-                ap_password,
-                WifiEnums.WIFI_CONFIG_APBAND_5G)
+            wutils.start_wifi_tethering(self.dut, ap_ssid, ap_password,
+                                        WifiEnums.WIFI_CONFIG_APBAND_5G)
             wutils.start_wifi_connection_scan_and_ensure_network_found(
                 self.dut_client, ap_ssid)
             wutils.stop_wifi_tethering(self.dut)
@@ -457,12 +509,18 @@
             time.sleep(2)
             cur_wifi_state = self.dut.droid.wifiCheckState()
             if initial_wifi_state != cur_wifi_state:
-               raise signals.TestFailure("Wifi state was %d before softAP and %d now!" %
+                raise signals.TestFailure(
+                    "Wifi state was %d before softAP and %d now!" %
                     (initial_wifi_state, cur_wifi_state),
-                        extras={"Iterations":"%d" % self.stress_count,
-                            "Pass":"%d" %count})
-        raise signals.TestPass(details="", extras={"Iterations":"%d" %
-            self.stress_count, "Pass":"%d" %(count+1)})
+                    extras={
+                        "Iterations": "%d" % self.stress_count,
+                        "Pass": "%d" % count
+                    })
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % (count + 1)
+                               })
 
     @test_tracker_info(uuid="eb22e26b-95d1-4580-8c76-85dfe6a42a0f")
     def test_stress_wifi_roaming(self):
@@ -471,22 +529,30 @@
         wutils.set_attns(self.attenuators, "AP1_on_AP2_off")
         self.scan_and_connect_by_ssid(self.dut, AP1_network)
         # Reduce iteration to half because each iteration does two roams.
-        for count in range(int(self.stress_count/2)):
+        for count in range(int(self.stress_count / 2)):
             self.log.info("Roaming iteration %d, from %s to %s", count,
-                           AP1_network, AP2_network)
+                          AP1_network, AP2_network)
             try:
                 wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
-                    "AP1_off_AP2_on", AP2_network)
+                                                    "AP1_off_AP2_on",
+                                                    AP2_network)
                 self.log.info("Roaming iteration %d, from %s to %s", count,
-                               AP2_network, AP1_network)
+                              AP2_network, AP1_network)
                 wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
-                    "AP1_on_AP2_off", AP1_network)
+                                                    "AP1_on_AP2_off",
+                                                    AP1_network)
             except:
                 raise signals.TestFailure("Roaming failed. Look at logs",
-                    extras={"Iterations":"%d" %self.stress_count, "Pass":"%d" %
-                        (count*2)})
-        raise signals.TestPass(details="", extras={"Iterations":"%d" %
-            self.stress_count, "Pass":"%d" %((count+1)*2)})
+                                          extras={
+                                              "Iterations":
+                                              "%d" % self.stress_count,
+                                              "Pass": "%d" % (count * 2)
+                                          })
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % ((count + 1) * 2)
+                               })
 
     @test_tracker_info(uuid="e8ae8cd2-c315-4c08-9eb3-83db65b78a58")
     def test_stress_network_selector_2G_connection(self):
@@ -503,13 +569,17 @@
         networks = [self.reference_networks[0]['2g']]
         self.add_networks(self.dut, networks)
         for count in range(self.stress_count):
-            self.connect_and_verify_connected_ssid(self.reference_networks[0]['2g'])
+            self.connect_and_verify_connected_ssid(
+                self.reference_networks[0]['2g'])
             # move the DUT out of range
             self.attenuators[0].set_atten(95)
             time.sleep(10)
         wutils.set_attns(self.attenuators, "default")
-        raise signals.TestPass(details="", extras={"Iterations":"%d" %
-            self.stress_count, "Pass":"%d" %(count+1)})
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % (count + 1)
+                               })
 
     @test_tracker_info(uuid="5d5d14cb-3cd1-4b3d-8c04-0d6f4b764b6b")
     def test_stress_pno_connection_to_2g(self):
@@ -524,8 +594,7 @@
         6. Repeat step 4-5
         """
         self.scan_and_connect_by_ssid(self.dut, self.wpa_2g)
-        wutils.wifi_forget_network(
-                self.dut, self.wpa_2g[WifiEnums.SSID_KEY])
+        wutils.wifi_forget_network(self.dut, self.wpa_2g[WifiEnums.SSID_KEY])
         networks = [self.reference_networks[0]['2g']]
         for attenuator in self.attenuators:
             attenuator.set_atten(95)
@@ -534,16 +603,20 @@
         self.dut.droid.wakeLockRelease()
         self.dut.droid.goToSleepNow()
         for count in range(self.stress_count):
-            self.connect_and_verify_connected_ssid(self.reference_networks[0]['2g'], is_pno=True)
-            wutils.wifi_forget_network(
-                    self.dut, networks[0][WifiEnums.SSID_KEY])
+            self.connect_and_verify_connected_ssid(
+                self.reference_networks[0]['2g'], is_pno=True)
+            wutils.wifi_forget_network(self.dut,
+                                       networks[0][WifiEnums.SSID_KEY])
             # move the DUT out of range
             self.attenuators[0].set_atten(95)
             time.sleep(10)
             self.add_networks(self.dut, networks)
         wutils.set_attns(self.attenuators, "default")
-        raise signals.TestPass(details="", extras={"Iterations":"%d" %
-            self.stress_count, "Pass":"%d" %(count+1)})
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % (count + 1)
+                               })
 
     @test_tracker_info(uuid="c880e742-8d20-4134-b717-5b6d45f6c337")
     def test_2g_sta_wifi_on_off_under_airplane_mode(self):
@@ -564,18 +637,25 @@
                 wutils.wifi_toggle_state(self.dut, True)
                 startup_time = time.time() - startTime
                 self.log.debug("WiFi was enabled on the device in %s s." %
-                    startup_time)
+                               startup_time)
                 time.sleep(DEFAULT_TIMEOUT)
                 # Start IPerf traffic from phone to server.
                 # Upload data for 10s.
                 args = "-p {} -t {}".format(self.iperf_server_port, 10)
                 self.log.info("Running iperf client {}".format(args))
-                result, data = self.dut.run_iperf_client(self.iperf_server_address, args)
+                result, data = self.dut.run_iperf_client(
+                    self.iperf_server_address, args)
                 if not result:
                     self.log.debug("Error occurred in iPerf traffic.")
                     self.run_ping(10)
             except:
-                signals.TestFailure(details="", extras={"Iterations":"%d" %
-                    self.stress_count, "Pass":"%d" %count})
-        raise signals.TestPass(details="", extras={"Iterations":"%d" %
-            self.stress_count, "Pass":"%d" %(count+1)})
\ No newline at end of file
+                signals.TestFailure(details="",
+                                    extras={
+                                        "Iterations": "%d" % self.stress_count,
+                                        "Pass": "%d" % count
+                                    })
+        raise signals.TestPass(details="",
+                               extras={
+                                   "Iterations": "%d" % self.stress_count,
+                                   "Pass": "%d" % (count + 1)
+                               })
diff --git a/acts_tests/tests/google/wifi/WifiTeleCoexTest.py b/acts_tests/tests/google/wifi/WifiTeleCoexTest.py
index b3ba3eb..0ddbaa1 100644
--- a/acts_tests/tests/google/wifi/WifiTeleCoexTest.py
+++ b/acts_tests/tests/google/wifi/WifiTeleCoexTest.py
@@ -1,6 +1,5 @@
 #!/usr/bin/env python3.4
 
-import queue
 import time
 
 import acts.base_test
@@ -36,10 +35,10 @@
 STRESS_COUNT = "stress_iteration"
 DEFAULT_TIMEOUT = 10
 
+
 class WifiTeleCoexTest(TelephonyBaseTest):
     """Tests for WiFi, Celular Co-existance."""
 
-
     def setup_class(self):
         TelephonyBaseTest.setup_class(self)
 
@@ -53,12 +52,12 @@
         self.dut = self.android_devices[0]
         self.wifi_network_ssid = self.user_params.get(WIFI_SSID)
         self.wifi_network_pass = self.user_params.get(WIFI_PWD)
-        self.network = { WifiEnums.SSID_KEY : self.wifi_network_ssid,
-                         WifiEnums.PWD_KEY : self.wifi_network_pass
-                       }
+        self.network = {
+            WifiEnums.SSID_KEY: self.wifi_network_ssid,
+            WifiEnums.PWD_KEY: self.wifi_network_pass
+        }
         self.stress_count = self.user_params.get(STRESS_COUNT)
 
-
     def setup_test(self):
         """ Setup test make sure the DUT is wake and screen unlock"""
         for ad in self.android_devices:
@@ -67,7 +66,6 @@
             ad.droid.telephonyToggleDataConnection(True)
         wifi_utils.wifi_toggle_state(self.dut, True)
 
-
     def teardown_test(self):
         """ End test make sure the DUT return idle"""
         for ad in self.android_devices:
@@ -78,7 +76,6 @@
 
     """Helper Functions"""
 
-
     def connect_to_wifi(self, ad, network):
         """Connection logic for open and psk wifi networks.
 
@@ -90,12 +87,11 @@
         ad.ed.clear_all_events()
         wifi_utils.start_wifi_connection_scan(ad)
         scan_results = ad.droid.wifiGetScanResults()
-        wifi_utils.assert_network_in_list({WifiEnums.SSID_KEY:
-                self.wifi_network_ssid}, scan_results)
+        wifi_utils.assert_network_in_list(
+            {WifiEnums.SSID_KEY: self.wifi_network_ssid}, scan_results)
         wifi_utils.wifi_connect(ad, network)
-        self.log.debug("Connected to %s network on %s device" % (
-                network[WifiEnums.SSID_KEY], ad.serial))
-
+        self.log.debug("Connected to %s network on %s device" %
+                       (network[WifiEnums.SSID_KEY], ad.serial))
 
     def stress_toggle_wifi(self, stress_count):
         """Toggle WiFi in a loop.
@@ -112,7 +108,6 @@
             raise signals.TestFailure("WiFi did not turn on after toggling it"
                                       " %d times" % self.stress_count)
 
-
     def stress_toggle_airplane(self, stress_count):
         """Toggle Airplane mode in a loop.
 
@@ -128,7 +123,6 @@
             raise signals.TestFailure("WiFi did not turn on after toggling it"
                                       " %d times" % self.stress_count)
 
-
     def stress_toggle_airplane_and_wifi(self, stress_count):
         """Toggle Airplane and WiFi modes in a loop.
 
@@ -138,7 +132,8 @@
 
         """
         for count in range(stress_count):
-            self.log.debug("stress_toggle_airplane_and_wifi: Iteration %d" % count)
+            self.log.debug("stress_toggle_airplane_and_wifi: Iteration %d" %
+                           count)
             self.log.debug("Toggling Airplane mode ON")
             asserts.assert_true(
                 acts.utils.force_airplane_mode(self.dut, True),
@@ -150,7 +145,8 @@
             # Sleep for 1s before getting new WiFi state.
             time.sleep(1)
             if not self.dut.droid.wifiGetisWifiEnabled():
-                raise signals.TestFailure("WiFi did not turn on after turning ON"
+                raise signals.TestFailure(
+                    "WiFi did not turn on after turning ON"
                     " Airplane mode")
             asserts.assert_true(
                 acts.utils.force_airplane_mode(self.dut, False),
@@ -160,7 +156,6 @@
             raise signals.TestFailure("WiFi did not turn on after toggling it"
                                       " %d times" % self.stress_count)
 
-
     def setup_cellular_voice_calling(self):
         """Setup phone for voice general calling and make sure phone is
            attached to voice."""
@@ -171,7 +166,6 @@
                                           " calling serial:%s" % ad.serial)
         self.log.debug("Finished setting up phones for voice calling")
 
-
     def validate_cellular_and_wifi(self):
         """Validate WiFi, make some cellular calls.
 
@@ -185,13 +179,13 @@
         time.sleep(30)
         wifi_info = self.dut.droid.wifiGetConnectionInfo()
         if wifi_info[WifiEnums.SSID_KEY] != self.wifi_network_ssid:
-            raise signals.TestFailure("Phone failed to connect to %s network on"
-                                      " %s" % (self.wifi_network_ssid,
-                                      self.dut.serial))
+            raise signals.TestFailure(
+                "Phone failed to connect to %s network on"
+                " %s" % (self.wifi_network_ssid, self.dut.serial))
 
         # Make short call sequence between Phone A and Phone B.
-        two_phone_call_short_seq(self.log, self.ads[0], None, None, self.ads[1],
-                                 None, None)
+        two_phone_call_short_seq(self.log, self.ads[0], None, None,
+                                 self.ads[1], None, None)
 
     def _phone_idle_iwlan(self):
         return phone_idle_iwlan(self.log, self.android_devices[0])
@@ -212,32 +206,30 @@
           (failure is logged) True otherwise.
 
         """
-        tele_utils.toggle_airplane_mode(self.log, self.android_devices[0], False)
+        tele_utils.toggle_airplane_mode(self.log, self.android_devices[0],
+                                        False)
         toggle_volte(self.log, self.android_devices[0], volte_mode)
-        if not ensure_network_generation(
-                self.log,
-                self.android_devices[0],
-                GEN_4G,
-                voice_or_data=NETWORK_SERVICE_DATA):
+        if not ensure_network_generation(self.log,
+                                         self.android_devices[0],
+                                         GEN_4G,
+                                         voice_or_data=NETWORK_SERVICE_DATA):
             return False
         if not set_wfc_mode(self.log, self.android_devices[0], wfc_mode):
             self.log.error("{} set WFC mode failed.".format(
                 self.android_devices[0].serial))
             return False
         tele_utils.toggle_airplane_mode(self.log, self.android_devices[0],
-                             is_airplane_mode)
-        if not tel_wifi_utils.ensure_wifi_connected(self.log, self.android_devices[0],
-                                     self.wifi_network_ssid,
-                                     self.wifi_network_pass):
+                                        is_airplane_mode)
+        if not tel_wifi_utils.ensure_wifi_connected(
+                self.log, self.android_devices[0], self.wifi_network_ssid,
+                self.wifi_network_pass):
             self.log.error("{} connect WiFI failed".format(
                 self.android_devices[0].serial))
             return False
         return True
 
-
     """Tests"""
 
-
     @test_tracker_info(uuid="8b9b6fb9-964b-43e7-b75f-675774ee346f")
     @TelephonyBaseTest.tel_test_wrap
     def test_toggle_wifi_call(self):
@@ -259,7 +251,6 @@
         self.validate_cellular_and_wifi()
         return True
 
-
     @test_tracker_info(uuid="caf22447-6354-4a2e-99e5-0ff235fc8f20")
     @TelephonyBaseTest.tel_test_wrap
     def test_toggle_airplane_call(self):
@@ -281,7 +272,6 @@
         self.validate_cellular_and_wifi()
         return True
 
-
     @test_tracker_info(uuid="dd888b35-f820-409a-89af-4b0f6551e4d6")
     @TelephonyBaseTest.tel_test_wrap
     def test_toggle_airplane_and_wifi_call(self):
@@ -305,7 +295,6 @@
         self.validate_cellular_and_wifi()
         return True
 
-
     @test_tracker_info(uuid="15db5b7e-827e-4bc8-8e77-7fcce343a323")
     @TelephonyBaseTest.tel_test_wrap
     def test_stress_toggle_wifi_call(self):
@@ -327,7 +316,6 @@
         self.validate_cellular_and_wifi()
         return True
 
-
     @test_tracker_info(uuid="80a2f1bf-5e41-453a-9b8e-be3b41d4d313")
     @TelephonyBaseTest.tel_test_wrap
     def test_stress_toggle_airplane_call(self):
@@ -349,7 +337,6 @@
         self.validate_cellular_and_wifi()
         return True
 
-
     @test_tracker_info(uuid="b88ad3e7-6462-4280-ad57-22d0ac91fdd8")
     @TelephonyBaseTest.tel_test_wrap
     def test_stress_toggle_airplane_and_wifi_call(self):
@@ -377,7 +364,6 @@
     @test_tracker_info(uuid="7cd9698c-7cde-4c99-b73a-67a2246ca4ec")
     @TelephonyBaseTest.tel_test_wrap
     def test_toggle_WFC_call(self):
-
         """Test to toggle WiFi and then perform WiFi connection and
            cellular calls.
 
@@ -397,7 +383,7 @@
             The device is using WiFi calling to call out.
 
         """
-        mo_mt=[]
+        mo_mt = []
         if mo_mt == DIRECTION_MOBILE_ORIGINATED:
             ad_caller = self.ads[0]
             ad_callee = self.ads[1]
@@ -411,7 +397,7 @@
         self.connect_to_wifi(self.dut, self.network)
         asserts.assert_true(
             acts.utils.force_airplane_mode(self.dut, True),
-                "Can not turn on airplane mode on: %s" % self.dut.serial)
+            "Can not turn on airplane mode on: %s" % self.dut.serial)
         time.sleep(1)
         self.log.info("Toggling wifi ON")
         wifi_utils.wifi_toggle_state(self.dut, True)
@@ -421,13 +407,13 @@
         if not self._phone_idle_iwlan():
             self.log.error("no in wifi calling")
             raise signals.TestFailure("The Wifi calling test is failed."
-                "WiFi State = %d" %self.dut.droid.wifiCheckState())
+                                      "WiFi State = %d" %
+                                      self.dut.droid.wifiCheckState())
         tele_utils.hangup_call(self.log, self.ads[0])
 
     @test_tracker_info(uuid="c1f0e0a7-b651-4d6c-a4a5-f946cabf56ef")
     @TelephonyBaseTest.tel_test_wrap
     def test_back_to_back_of_the_modem_restart(self):
-
         """Make sure DUT can connect to AP after modem restart
 
         Raises:
@@ -461,13 +447,13 @@
             time.sleep(20)
             self.connect_to_wifi(self.dut, self.network)
         except:
-            raise signals.TestFailure("The Wifi connect failed after modem restart."
-                "WiFi State = %d" %self.dut.droid.wifiCheckState())
+            raise signals.TestFailure(
+                "The Wifi connect failed after modem restart."
+                "WiFi State = %d" % self.dut.droid.wifiCheckState())
 
     @test_tracker_info(uuid="a72ff9da-3855-4c21-b447-b80f43227961")
     @TelephonyBaseTest.tel_test_wrap
     def test_internet_accessing_over_wifi_and_mms_test(self):
-
         """Verify when MMS is working WLAN connection can work normally as well.
 
         Raises:
@@ -492,15 +478,14 @@
             mms_utils._mms_test(self.log, self.ads)
             if not tele_utils.verify_internet_connection(
                     self.dut2.log, self.dut2):
-                    raise signals.TestFailure("The internet connection is stop."
-                        "Current WiFi state is %d"
-                        % self.dut2.droid.wifiCheckState())
+                raise signals.TestFailure("The internet connection is stop."
+                                          "Current WiFi state is %d" %
+                                          self.dut2.droid.wifiCheckState())
             time.sleep(30)
 
     @test_tracker_info(uuid="a7d774e4-ead3-465c-b4a6-f39a6397dfe3")
     @TelephonyBaseTest.tel_test_wrap
     def test_internet_accessing_wifi_and_data_test(self):
-
         """Verify interwork between Wi-Fi and data.
 
         Raises:
@@ -525,8 +510,8 @@
                     self.dut.log, self.dut):
                 raise signals.TestFailure(
                     "The internet connection is stop"
-                    "for WiFi off. Current WiFi state is %d"
-                    % self.dut.droid.wifiCheckState())
+                    "for WiFi off. Current WiFi state is %d" %
+                    self.dut.droid.wifiCheckState())
             wifi_utils.wifi_toggle_state(self.dut, True)
             time.sleep(DEFAULT_TIMEOUT)
             self.dut.log.info("DUT data is disable")
@@ -536,8 +521,8 @@
                     self.dut.log, self.dut):
                 raise signals.TestFailure(
                     "The internet connection is stop"
-                    "for Data off. Current WiFi state is %d"
-                    % self.dut.droid.wifiCheckState())
+                    "for Data off. Current WiFi state is %d" %
+                    self.dut.droid.wifiCheckState())
             self.dut.log.info("DUT data is enable")
             self.dut.droid.telephonyToggleDataConnection(True)
             time.sleep(DEFAULT_TIMEOUT)
@@ -545,7 +530,6 @@
     @test_tracker_info(uuid="e53adef6-d537-4098-a354-1e63457ab444")
     @TelephonyBaseTest.tel_test_wrap
     def test_internet_accessing_wifi_and_usb_tethering(self):
-
         """Verify interwork between Wi-Fi and USB_TETHERED.
 
         Raises:
@@ -570,13 +554,15 @@
             time.sleep(DEFAULT_TIMEOUT)
             if not tele_utils.verify_internet_connection(
                     self.dut.log, self.dut):
-                raise signals.TestFailure("The internet connection is stop"
-                    "for tethering enable. Current WiFi state is %d"
-                    % self.dut.droid.wifiCheckState())
+                raise signals.TestFailure(
+                    "The internet connection is stop"
+                    "for tethering enable. Current WiFi state is %d" %
+                    self.dut.droid.wifiCheckState())
             nutil.stop_usb_tethering(self.dut)
             time.sleep(DEFAULT_TIMEOUT)
             if not tele_utils.verify_internet_connection(
                     self.dut.log, self.dut):
-                raise signals.TestFailure("The internet connection is stop"
-                    "for tethering disable. Current WiFi state is %d"
-                    % self.dut.droid.wifiCheckState())
+                raise signals.TestFailure(
+                    "The internet connection is stop"
+                    "for tethering disable. Current WiFi state is %d" %
+                    self.dut.droid.wifiCheckState())
diff --git a/acts_tests/tests/google/wifi/WifiTetheringTest.py b/acts_tests/tests/google/wifi/WifiTetheringTest.py
index 730bae3..b212b9b 100644
--- a/acts_tests/tests/google/wifi/WifiTetheringTest.py
+++ b/acts_tests/tests/google/wifi/WifiTetheringTest.py
@@ -13,7 +13,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import logging
 import random
 import socket
 import time
@@ -35,6 +34,7 @@
 
 WAIT_TIME = 5
 
+
 class WifiTetheringTest(WifiBaseTest):
     """ Tests for Wifi Tethering """
 
@@ -45,8 +45,10 @@
         self.tethered_devices = self.android_devices[1:]
         req_params = ("url", "open_network", "wifi6_models")
         self.unpack_userparams(req_params)
-        self.network = {"SSID": "hotspot_%s" % utils.rand_ascii_str(6),
-                        "password": "pass_%s" % utils.rand_ascii_str(6)}
+        self.network = {
+            "SSID": "hotspot_%s" % utils.rand_ascii_str(6),
+            "password": "pass_%s" % utils.rand_ascii_str(6)
+        }
         self.new_ssid = "hs_%s" % utils.rand_ascii_str(6)
 
         nutils.verify_lte_data_and_tethering_supported(self.hotspot_device)
@@ -95,11 +97,13 @@
             False: if not
         """
         # Currently only Verizon support IPv6 tethering
-        carrier_supports_tethering = ["vzw", "tmo", "Far EasTone", "Chunghwa Telecom"]
+        carrier_supports_tethering = [
+            "vzw", "tmo", "Far EasTone", "Chunghwa Telecom"
+        ]
         operator = get_operator_name(self.log, dut)
         return operator in carrier_supports_tethering
 
-    def _carrier_supports_ipv6(self,dut):
+    def _carrier_supports_ipv6(self, dut):
         """ Verify if carrier supports ipv6
             Currently, only verizon and t-mobile supports IPv6
 
@@ -107,7 +111,9 @@
             True: if carrier supports ipv6
             False: if not
         """
-        carrier_supports_ipv6 = ["vzw", "tmo", "Far EasTone", "Chunghwa Telecom"]
+        carrier_supports_ipv6 = [
+            "vzw", "tmo", "Far EasTone", "Chunghwa Telecom"
+        ]
         operator = get_operator_name(self.log, dut)
         self.log.info("Carrier is %s" % operator)
         return operator in carrier_supports_ipv6
@@ -119,8 +125,9 @@
         active_link_addrs = dut.droid.connectivityGetAllAddressesOfActiveLink()
         if dut==self.hotspot_device and self._carrier_supports_ipv6(dut)\
             or self._supports_ipv6_tethering(self.hotspot_device):
-            asserts.assert_true(self._is_ipaddress_ipv6(http_response),
-                                "The http response did not return IPv6 address")
+            asserts.assert_true(
+                self._is_ipaddress_ipv6(http_response),
+                "The http response did not return IPv6 address")
             asserts.assert_true(
                 active_link_addrs and http_response in str(active_link_addrs),
                 "Could not find IPv6 address in link properties")
@@ -148,9 +155,9 @@
         """ Randomly connect and disconnect devices from the
             self.tethered_devices list to hotspot device
         """
-        device_connected = [ False ] * len(self.tethered_devices)
+        device_connected = [False] * len(self.tethered_devices)
         for _ in range(50):
-            dut_id = random.randint(0, len(self.tethered_devices)-1)
+            dut_id = random.randint(0, len(self.tethered_devices) - 1)
             dut = self.tethered_devices[dut_id]
             # wait for 1 sec between connect & disconnect stress test
             time.sleep(1)
@@ -201,13 +208,13 @@
         total_devices = num_android_devices + num_wifi_dongles
         device_connected = [False] * total_devices
         for _ in range(50):
-            dut_id = random.randint(0, total_devices-1)
+            dut_id = random.randint(0, total_devices - 1)
             wifi_state = device_connected[dut_id]
             if dut_id < num_android_devices:
-              self._connect_disconnect_android_device(dut_id, wifi_state)
+                self._connect_disconnect_android_device(dut_id, wifi_state)
             else:
-              self._connect_disconnect_wifi_dongle(dut_id-num_android_devices,
-                                                   wifi_state)
+                self._connect_disconnect_wifi_dongle(
+                    dut_id - num_android_devices, wifi_state)
             device_connected[dut_id] = not device_connected[dut_id]
 
     def _verify_ping(self, dut, ip, isIPv6=False):
@@ -252,10 +259,10 @@
         remote_ip = wd.ip_address()
         port = 8888
 
-        time.sleep(6) # wait until UDP packets method is invoked
+        time.sleep(6)  # wait until UDP packets method is invoked
         socket = sutils.open_datagram_socket(ad, local_ip, port)
-        sutils.send_recv_data_datagram_sockets(
-            ad, ad, socket, socket, remote_ip, port)
+        sutils.send_recv_data_datagram_sockets(ad, ad, socket, socket,
+                                               remote_ip, port)
         sutils.close_datagram_socket(ad, socket)
 
     def _ping_hotspot_interfaces_from_tethered_device(self, dut):
@@ -274,8 +281,8 @@
             iface_name = interface.split()[0].split(':')[1]
             if iface_name == "lo":
                 continue
-            ip_list = self._return_ip_for_interface(
-                self.hotspot_device, iface_name)
+            ip_list = self._return_ip_for_interface(self.hotspot_device,
+                                                    iface_name)
             for ip in ip_list:
                 ping_result = self._verify_ping(dut, ip)
                 self.log.info("Ping result: %s %s %s" %
@@ -294,8 +301,11 @@
         asserts.assert_true(ad.droid.wifiSetWifiApConfiguration(config),
                             "Failed to set WifiAp Configuration")
         wifi_ap = ad.droid.wifiGetApConfiguration()
-        asserts.assert_true(wifi_ap[wutils.WifiEnums.SSID_KEY] == config[wutils.WifiEnums.SSID_KEY],
-                            "Configured wifi hotspot SSID does not match with the expected SSID")
+        asserts.assert_true(
+            wifi_ap[wutils.WifiEnums.SSID_KEY] == config[
+                wutils.WifiEnums.SSID_KEY],
+            "Configured wifi hotspot SSID does not match with the expected SSID"
+        )
 
     def _turn_on_wifi_hotspot(self, ad):
         """ Turn on wifi hotspot with a config that is already saved
@@ -307,8 +317,8 @@
         ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
         try:
             ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
-            ad.ed.wait_for_event("TetherStateChanged",
-                                  lambda x: x["data"]["ACTIVE_TETHER"], 30)
+            ad.ed.wait_for_event(
+                "TetherStateChanged", lambda x: x["data"]["ACTIVE_TETHER"], 30)
         except:
             asserts.fail("Didn't receive wifi tethering starting confirmation")
         ad.droid.wifiStopTrackingTetherStateChange()
@@ -363,15 +373,17 @@
         self.hotspot_device.droid.telephonyToggleDataConnection(False)
         asserts.assert_equal(
             self.hotspot_device.droid.telephonyGetDataConnectionState(),
-            tel_defines.DATA_STATE_CONNECTED,
-            "Could not disable cell data")
+            tel_defines.DATA_STATE_CONNECTED, "Could not disable cell data")
 
-        time.sleep(WAIT_TIME) # wait until the IPv6 is removed from link properties
+        time.sleep(
+            WAIT_TIME)  # wait until the IPv6 is removed from link properties
 
-        result = self.tethered_devices[0].droid.connectivityHasIPv6DefaultRoute()
+        result = self.tethered_devices[
+            0].droid.connectivityHasIPv6DefaultRoute()
         self.hotspot_device.droid.telephonyToggleDataConnection(True)
         if result:
-            asserts.fail("Found IPv6 default route in link properties:Data off")
+            asserts.fail(
+                "Found IPv6 default route in link properties:Data off")
         self.log.info("Did not find IPv6 address in link properties")
 
         # Disable wifi tethering
@@ -389,8 +401,8 @@
                         "No wifi dongles connected. Skipping test")
         wutils.toggle_wifi_off_and_on(self.hotspot_device)
         self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G)
-        self._test_traffic_between_two_tethered_devices(self.tethered_devices[0],
-                                                        self.arduino_wifi_dongles[0])
+        self._test_traffic_between_two_tethered_devices(
+            self.tethered_devices[0], self.arduino_wifi_dongles[0])
         wutils.stop_wifi_tethering(self.hotspot_device)
 
     @test_tracker_info(uuid="953f6e2e-27bd-4b73-85a6-d2eaa4e755d5")
@@ -403,8 +415,8 @@
         """
         wutils.toggle_wifi_off_and_on(self.hotspot_device)
         self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G)
-        self._test_traffic_between_two_tethered_devices(self.tethered_devices[0],
-                                                        self.arduino_wifi_dongles[0])
+        self._test_traffic_between_two_tethered_devices(
+            self.tethered_devices[0], self.arduino_wifi_dongles[0])
         wutils.stop_wifi_tethering(self.hotspot_device)
 
     @test_tracker_info(uuid="d7d5aa51-682d-4882-a334-61966d93b68c")
@@ -446,8 +458,7 @@
                                        self.network,
                                        check_connectivity=True)
         wutils.verify_11ax_softap(self.hotspot_device,
-                                  self.tethered_devices[0],
-                                  self.wifi6_models)
+                                  self.tethered_devices[0], self.wifi6_models)
 
     @test_tracker_info(uuid="17e450f4-795f-4e67-adab-984940dffedc")
     def test_wifi_tethering_wpapsk_network_5g(self):
@@ -462,8 +473,7 @@
                                        self.network,
                                        check_connectivity=True)
         wutils.verify_11ax_softap(self.hotspot_device,
-                                  self.tethered_devices[0],
-                                  self.wifi6_models)
+                                  self.tethered_devices[0], self.wifi6_models)
 
     @test_tracker_info(uuid="2bc344cb-0277-4f06-b6cc-65b3972086ed")
     def test_change_wifi_hotspot_ssid_when_hotspot_enabled(self):
@@ -487,12 +497,12 @@
             "Configured wifi hotspot SSID did not match with the expected SSID")
         wutils.connect_to_wifi_network(self.tethered_devices[0], self.network)
         wutils.verify_11ax_softap(self.hotspot_device,
-                                  self.tethered_devices[0],
-                                  self.wifi6_models)
+                                  self.tethered_devices[0], self.wifi6_models)
 
         # update the wifi ap configuration with new ssid
         config = {wutils.WifiEnums.SSID_KEY: self.new_ssid}
-        config[wutils.WifiEnums.PWD_KEY] = self.network[wutils.WifiEnums.PWD_KEY]
+        config[wutils.WifiEnums.PWD_KEY] = self.network[
+            wutils.WifiEnums.PWD_KEY]
         config[wutils.WifiEnums.AP_BAND_KEY] = WIFI_CONFIG_APBAND_2G
         self._save_wifi_softap_configuration(dut, config)
 
@@ -506,8 +516,7 @@
                        self.network[wutils.WifiEnums.PWD_KEY]}
         wutils.connect_to_wifi_network(self.tethered_devices[0], new_network)
         wutils.verify_11ax_softap(self.hotspot_device,
-                                  self.tethered_devices[0],
-                                  self.wifi6_models)
+                                  self.tethered_devices[0], self.wifi6_models)
 
     @test_tracker_info(uuid="4cf7ab26-ca2d-46f6-9d3f-a935c7e04c97")
     def test_wifi_tethering_open_network_2g(self):
@@ -521,17 +530,14 @@
         open_network = {
             wutils.WifiEnums.SSID_KEY: "hs_2g_%s" % utils.rand_ascii_str(6)
         }
-        wutils.start_wifi_tethering(
-            self.hotspot_device,
-            open_network[wutils.WifiEnums.SSID_KEY],
-            None,
-            WIFI_CONFIG_APBAND_2G)
+        wutils.start_wifi_tethering(self.hotspot_device,
+                                    open_network[wutils.WifiEnums.SSID_KEY],
+                                    None, WIFI_CONFIG_APBAND_2G)
         wutils.connect_to_wifi_network(self.tethered_devices[0],
                                        open_network,
                                        check_connectivity=True)
         wutils.verify_11ax_softap(self.hotspot_device,
-                                  self.tethered_devices[0],
-                                  self.wifi6_models)
+                                  self.tethered_devices[0], self.wifi6_models)
 
     @test_tracker_info(uuid="f407049b-1324-40ea-a8d1-f90587933310")
     def test_wifi_tethering_open_network_5g(self):
@@ -545,17 +551,14 @@
         open_network = {
             wutils.WifiEnums.SSID_KEY: "hs_5g_%s" % utils.rand_ascii_str(6)
         }
-        wutils.start_wifi_tethering(
-            self.hotspot_device,
-            open_network[wutils.WifiEnums.SSID_KEY],
-            None,
-            WIFI_CONFIG_APBAND_5G)
+        wutils.start_wifi_tethering(self.hotspot_device,
+                                    open_network[wutils.WifiEnums.SSID_KEY],
+                                    None, WIFI_CONFIG_APBAND_5G)
         wutils.connect_to_wifi_network(self.tethered_devices[0],
                                        open_network,
                                        check_connectivity=True)
         wutils.verify_11ax_softap(self.hotspot_device,
-                                  self.tethered_devices[0],
-                                  self.wifi6_models)
+                                  self.tethered_devices[0], self.wifi6_models)
 
     @test_tracker_info(uuid="d964f2e6-0acb-417c-ada9-eb9fc5a470e4")
     def test_wifi_tethering_open_network_2g_stress(self):
@@ -569,17 +572,20 @@
             5. Repeat steps 2 to 4
         """
         # save open network wifi ap configuration with 2G band
-        config = {wutils.WifiEnums.SSID_KEY:
-                  self.open_network[wutils.WifiEnums.SSID_KEY]}
+        config = {
+            wutils.WifiEnums.SSID_KEY:
+            self.open_network[wutils.WifiEnums.SSID_KEY]
+        }
         config[wutils.WifiEnums.AP_BAND_KEY] = WIFI_CONFIG_APBAND_2G
         self._save_wifi_softap_configuration(self.hotspot_device, config)
 
         # turn on/off wifi hotspot, connect device
         for _ in range(9):
             self._turn_on_wifi_hotspot(self.hotspot_device)
-            wutils.connect_to_wifi_network(self.tethered_devices[0], self.open_network)
+            wutils.connect_to_wifi_network(self.tethered_devices[0],
+                                           self.open_network)
             wutils.stop_wifi_tethering(self.hotspot_device)
-            time.sleep(1) # wait for some time before turning on hotspot
+            time.sleep(1)  # wait for some time before turning on hotspot
 
     @test_tracker_info(uuid="c7ef840c-4003-41fc-80e3-755f9057b542")
     def test_wifi_tethering_open_network_5g_stress(self):
@@ -593,14 +599,17 @@
             5. Repeat steps 2 to 4
         """
         # save open network wifi ap configuration with 5G band
-        config = {wutils.WifiEnums.SSID_KEY:
-                  self.open_network[wutils.WifiEnums.SSID_KEY]}
+        config = {
+            wutils.WifiEnums.SSID_KEY:
+            self.open_network[wutils.WifiEnums.SSID_KEY]
+        }
         config[wutils.WifiEnums.AP_BAND_KEY] = WIFI_CONFIG_APBAND_5G
         self._save_wifi_softap_configuration(self.hotspot_device, config)
 
         # turn on/off wifi hotspot, connect device
         for _ in range(9):
             self._turn_on_wifi_hotspot(self.hotspot_device)
-            wutils.connect_to_wifi_network(self.tethered_devices[0], self.open_network)
+            wutils.connect_to_wifi_network(self.tethered_devices[0],
+                                           self.open_network)
             wutils.stop_wifi_tethering(self.hotspot_device)
-            time.sleep(1) # wait for some time before turning on hotspot
+            time.sleep(1)  # wait for some time before turning on hotspot
diff --git a/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py b/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
index a8d9628..ddd25f7 100644
--- a/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
+++ b/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
@@ -50,6 +50,7 @@
     example config file to run this test class see
     example_connectivity_performance_ap_sta.json.
     """
+
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         # Define metrics to be uploaded to BlackBox
@@ -125,6 +126,7 @@
             except:
                 self.log.warning('Could not start sniffer. Disabling sniffs.')
                 self.testbed_params['sniffer_enable'] = 0
+        self.sniffer_subsampling = 1
         self.log_path = os.path.join(logging.log_path, 'test_results')
         os.makedirs(self.log_path, exist_ok=True)
         self.log.info('Access Point Configuration: {}'.format(
@@ -159,44 +161,18 @@
             test_result_dict: dict containing attenuation, throughput and other
             meta data
         """
-        avg_throughput = test_result['iperf_summary']['avg_throughput']
-        min_throughput = test_result['iperf_summary']['min_throughput']
-        std_dev_percent = (
-            test_result['iperf_summary']['std_deviation'] /
-            test_result['iperf_summary']['avg_throughput']) * 100
-        # Set blackbox metrics
-        if self.publish_testcase_metrics:
-            self.testcase_metric_logger.add_metric('avg_throughput',
-                                                   avg_throughput)
-            self.testcase_metric_logger.add_metric('min_throughput',
-                                                   min_throughput)
-            self.testcase_metric_logger.add_metric('std_dev_percent',
-                                                   std_dev_percent)
         # Evaluate pass/fail
         min_throughput_check = (
-            (min_throughput / avg_throughput) *
+            (test_result['iperf_summary']['min_throughput'] /
+             test_result['iperf_summary']['avg_throughput']) *
             100) > self.testclass_params['min_throughput_threshold']
-        std_deviation_check = std_dev_percent < self.testclass_params[
-            'std_deviation_threshold']
+        std_deviation_check = test_result['iperf_summary'][
+            'std_dev_percent'] < self.testclass_params[
+                'std_deviation_threshold']
 
-        llstats = (
-            'TX MCS = {0} ({1:.1f}%). '
-            'RX MCS = {2} ({3:.1f}%)'.format(
-                test_result['llstats']['summary']['common_tx_mcs'],
-                test_result['llstats']['summary']['common_tx_mcs_freq'] * 100,
-                test_result['llstats']['summary']['common_rx_mcs'],
-                test_result['llstats']['summary']['common_rx_mcs_freq'] * 100))
-
-        test_message = (
-            'Atten: {0:.2f}dB, RSSI: {1:.2f}dB. '
-            'Throughput (Mean: {2:.2f}, Std. Dev:{3:.2f}%, Min: {4:.2f} Mbps).'
-            'LLStats : {5}'.format(
-                test_result['attenuation'],
-                test_result['rssi_result']['signal_poll_rssi']['mean'],
-                avg_throughput, std_dev_percent, min_throughput, llstats))
         if min_throughput_check and std_deviation_check:
-            asserts.explicit_pass('Test Passed.' + test_message)
-        asserts.fail('Test Failed. ' + test_message)
+            asserts.explicit_pass('Test Passed.')
+        asserts.fail('Test Failed.')
 
     def post_process_results(self, test_result):
         """Extracts results and saves plots and JSON formatted results.
@@ -209,41 +185,37 @@
             avg throughput, other metrics, and other meta data
         """
         # Save output as text file
-        test_name = self.current_test_name
-        results_file_path = os.path.join(self.log_path,
-                                         '{}.txt'.format(test_name))
-        if test_result['iperf_result'].instantaneous_rates:
-            instantaneous_rates_Mbps = [
-                rate * 8 * (1.024**2)
-                for rate in test_result['iperf_result'].instantaneous_rates[
-                    self.testclass_params['iperf_ignored_interval']:-1]
-            ]
-            tput_standard_deviation = test_result[
-                'iperf_result'].get_std_deviation(
-                    self.testclass_params['iperf_ignored_interval']) * 8
-        else:
-            instantaneous_rates_Mbps = [float('nan')]
-            tput_standard_deviation = float('nan')
-        test_result['iperf_summary'] = {
-            'instantaneous_rates': instantaneous_rates_Mbps,
-            'avg_throughput': numpy.mean(instantaneous_rates_Mbps),
-            'std_deviation': tput_standard_deviation,
-            'min_throughput': min(instantaneous_rates_Mbps)
-        }
+        results_file_path = os.path.join(
+            self.log_path, '{}.txt'.format(self.current_test_name))
         with open(results_file_path, 'w') as results_file:
             json.dump(wputils.serialize_dict(test_result), results_file)
         # Plot and save
-        figure = BokehFigure(test_name,
-                             x_label='Time (s)',
-                             primary_y_label='Throughput (Mbps)')
-        time_data = list(range(0, len(instantaneous_rates_Mbps)))
-        figure.add_line(time_data,
-                        instantaneous_rates_Mbps,
-                        legend=self.current_test_name,
-                        marker='circle')
-        output_file_path = os.path.join(self.log_path,
-                                        '{}.html'.format(test_name))
-        figure.generate_figure(output_file_path)
+        # Set blackbox metrics
+        if self.publish_testcase_metrics:
+            self.testcase_metric_logger.add_metric(
+                'avg_throughput',
+                test_result['iperf_summary']['avg_throughput'])
+            self.testcase_metric_logger.add_metric(
+                'min_throughput',
+                test_result['iperf_summary']['min_throughput'])
+            self.testcase_metric_logger.add_metric(
+                'std_dev_percent',
+                test_result['iperf_summary']['std_dev_percent'])
+            figure = BokehFigure(self.current_test_name,
+                                 x_label='Time (s)',
+                                 primary_y_label='Throughput (Mbps)')
+            time_data = list(
+                range(
+                    0,
+                    len(test_result['iperf_summary']['instantaneous_rates'])))
+            figure.add_line(
+                time_data,
+                test_result['iperf_summary']['instantaneous_rates'],
+                legend=self.current_test_name,
+                marker='circle')
+            output_file_path = os.path.join(
+                self.log_path, '{}.html'.format(self.current_test_name))
+            figure.generate_figure(output_file_path)
         return test_result
 
     def setup_ap(self, testcase_params):
@@ -352,9 +324,11 @@
         # Run test and log result
         # Start iperf session
         self.log.info('Starting iperf test.')
+        test_result = collections.OrderedDict()
         llstats_obj = wputils.LinkLayerStats(self.dut)
         llstats_obj.update_stats()
-        if self.testbed_params['sniffer_enable']:
+        if self.testbed_params['sniffer_enable'] and len(
+                self.testclass_results) % self.sniffer_subsampling == 0:
             self.sniffer.start_capture(
                 network=testcase_params['test_network'],
                 chan=testcase_params['channel'],
@@ -375,7 +349,8 @@
         current_rssi = current_rssi.result()
         server_output_path = self.iperf_server.stop()
         # Stop sniffer
-        if self.testbed_params['sniffer_enable']:
+        if self.testbed_params['sniffer_enable'] and len(
+                self.testclass_results) % self.sniffer_subsampling == 0:
             self.sniffer.stop_capture()
         # Set attenuator to 0 dB
         for attenuator in self.attenuators:
@@ -388,16 +363,61 @@
         try:
             iperf_result = ipf.IPerfResult(iperf_file)
         except:
-            asserts.fail('Cannot get iperf result.')
+            iperf_result = ipf.IPerfResult('{}')  #empty iperf result
+            self.log.warning('Cannot get iperf result.')
+        if iperf_result.instantaneous_rates:
+            instantaneous_rates_Mbps = [
+                rate * 8 * (1.024**2)
+                for rate in iperf_result.instantaneous_rates[
+                    self.testclass_params['iperf_ignored_interval']:-1]
+            ]
+            tput_standard_deviation = iperf_result.get_std_deviation(
+                self.testclass_params['iperf_ignored_interval']) * 8
+        else:
+            instantaneous_rates_Mbps = [float('nan')]
+            tput_standard_deviation = float('nan')
+        test_result['iperf_summary'] = {
+            'instantaneous_rates':
+            instantaneous_rates_Mbps,
+            'avg_throughput':
+            numpy.mean(instantaneous_rates_Mbps),
+            'std_deviation':
+            tput_standard_deviation,
+            'min_throughput':
+            min(instantaneous_rates_Mbps),
+            'std_dev_percent':
+            (tput_standard_deviation / numpy.mean(instantaneous_rates_Mbps)) *
+            100
+        }
         llstats_obj.update_stats()
         curr_llstats = llstats_obj.llstats_incremental.copy()
-        test_result = collections.OrderedDict()
         test_result['testcase_params'] = testcase_params.copy()
         test_result['ap_settings'] = self.access_point.ap_settings.copy()
         test_result['attenuation'] = testcase_params['atten_level']
         test_result['iperf_result'] = iperf_result
         test_result['rssi_result'] = current_rssi
         test_result['llstats'] = curr_llstats
+
+        llstats = (
+            'TX MCS = {0} ({1:.1f}%). '
+            'RX MCS = {2} ({3:.1f}%)'.format(
+                test_result['llstats']['summary']['common_tx_mcs'],
+                test_result['llstats']['summary']['common_tx_mcs_freq'] * 100,
+                test_result['llstats']['summary']['common_rx_mcs'],
+                test_result['llstats']['summary']['common_rx_mcs_freq'] * 100))
+
+        test_message = (
+            'Atten: {0:.2f}dB, RSSI: {1:.2f}dB. '
+            'Throughput (Mean: {2:.2f}, Std. Dev:{3:.2f}%, Min: {4:.2f} Mbps).'
+            'LLStats : {5}'.format(
+                test_result['attenuation'],
+                test_result['rssi_result']['signal_poll_rssi']['mean'],
+                test_result['iperf_summary']['avg_throughput'],
+                test_result['iperf_summary']['std_dev_percent'],
+                test_result['iperf_summary']['min_throughput'], llstats))
+
+        self.log.info(test_message)
+
         self.testclass_results.append(test_result)
         return test_result
 
@@ -501,6 +521,7 @@
     setting turntable orientation and other chamber parameters to study
     performance in varying channel conditions
     """
+
     def __init__(self, controllers):
         base_test.BaseTestClass.__init__(self, controllers)
         # Define metrics to be uploaded to BlackBox
@@ -605,6 +626,22 @@
             test_atten = self.testclass_params['ota_atten_levels'][band][1]
         return test_atten
 
+    def _test_throughput_stability_over_orientation(self, testcase_params):
+        """ Function that gets called for each test case
+
+        The function gets called in each test case. The function customizes
+        the test based on the test name of the test that called it
+
+        Args:
+            testcase_params: dict containing test specific parameters
+        """
+        testcase_params = self.compile_test_params(testcase_params)
+        for position in testcase_params['positions']:
+            testcase_params['position'] = position
+            self.setup_throughput_stability_test(testcase_params)
+            test_result = self.run_throughput_stability_test(testcase_params)
+            self.post_process_results(test_result)
+
     def generate_test_cases(self, channels, modes, traffic_types,
                             traffic_directions, signal_levels, chamber_mode,
                             positions):
@@ -619,8 +656,8 @@
         }
 
         test_cases = []
-        for channel, mode, signal_level, position, traffic_type, traffic_direction in itertools.product(
-                channels, modes, signal_levels, positions, traffic_types,
+        for channel, mode, signal_level, traffic_type, traffic_direction in itertools.product(
+                channels, modes, signal_levels, traffic_types,
                 traffic_directions):
             bandwidth = int(''.join([x for x in mode if x.isdigit()]))
             if channel not in allowed_configs[bandwidth]:
@@ -634,19 +671,22 @@
                 signal_level=signal_level,
                 chamber_mode=chamber_mode,
                 total_positions=len(positions),
-                position=position)
+                positions=positions)
             testcase_name = ('test_tput_stability'
-                             '_{}_{}_{}_ch{}_{}_pos{}'.format(
+                             '_{}_{}_{}_ch{}_{}'.format(
                                  signal_level, traffic_type, traffic_direction,
-                                 channel, mode, position))
-            setattr(self, testcase_name,
-                    partial(self._test_throughput_stability, testcase_params))
+                                 channel, mode))
+            setattr(
+                self, testcase_name,
+                partial(self._test_throughput_stability_over_orientation,
+                        testcase_params))
             test_cases.append(testcase_name)
         return test_cases
 
 
 class WifiOtaThroughputStability_TenDegree_Test(WifiOtaThroughputStabilityTest
                                                 ):
+
     def __init__(self, controllers):
         WifiOtaThroughputStabilityTest.__init__(self, controllers)
         self.tests = self.generate_test_cases([6, 36, 149, '6g37'],
@@ -655,8 +695,13 @@
                                               ['high', 'low'], 'orientation',
                                               list(range(0, 360, 10)))
 
+    def setup_class(self):
+        WifiOtaThroughputStabilityTest.setup_class(self)
+        self.sniffer_subsampling = 6
+
 
 class WifiOtaThroughputStability_45Degree_Test(WifiOtaThroughputStabilityTest):
+
     def __init__(self, controllers):
         WifiOtaThroughputStabilityTest.__init__(self, controllers)
         self.tests = self.generate_test_cases([6, 36, 149, '6g37'],
@@ -668,6 +713,7 @@
 
 class WifiOtaThroughputStability_SteppedStirrers_Test(
         WifiOtaThroughputStabilityTest):
+
     def __init__(self, controllers):
         WifiOtaThroughputStabilityTest.__init__(self, controllers)
         self.tests = self.generate_test_cases([6, 36, 149, '6g37'],
@@ -676,3 +722,7 @@
                                               ['high', 'low'],
                                               'stepped stirrers',
                                               list(range(100)))
+
+    def setup_class(self):
+        WifiOtaThroughputStabilityTest.setup_class(self)
+        self.sniffer_subsampling = 10
diff --git a/acts_tests/tests/google/wifi/WifiTxPowerCheckTest.py b/acts_tests/tests/google/wifi/WifiTxPowerCheckTest.py
index 706903c..d0a3334 100644
--- a/acts_tests/tests/google/wifi/WifiTxPowerCheckTest.py
+++ b/acts_tests/tests/google/wifi/WifiTxPowerCheckTest.py
@@ -95,8 +95,8 @@
             test_types=[
                 'test_tx_power',
             ],
-            country_codes=['US', 'GB', 'JP'],
-            sar_states=range(0, 13))
+            country_codes=['US', 'GB', 'JP', 'CA', 'AU'],
+            sar_states=range(-1, 13))
 
     def setup_class(self):
         self.dut = self.android_devices[-1]
@@ -139,6 +139,9 @@
         self.nvram_sar_data = self.read_nvram_sar_data()
         self.csv_sar_data = self.read_sar_csv(self.testclass_params['sar_csv'])
 
+        # Configure test retries
+        self.user_params['retry_tests'] = [self.__class__.__name__]
+
     def teardown_class(self):
         # Turn WiFi OFF and reset AP
         self.access_point.teardown()
@@ -246,11 +249,13 @@
         of SAR scenarios to NVRAM data tables.
         """
 
-        self.sar_state_mapping = collections.OrderedDict([(-1, {
+        self.sar_state_mapping = collections.OrderedDict([(-2, {
             "google_name":
-            'WIFI_POWER_SCENARIO_DISABLE'
-        }), (0, {
+            'WIFI_POWER_SCENARIO_INVALID'
+        }), (-1, {
             "google_name": 'WIFI_POWER_SCENARIO_DISABLE'
+        }), (0, {
+            "google_name": 'WIFI_POWER_SCENARIO_VOICE_CALL'
         }), (1, {
             "google_name": 'WIFI_POWER_SCENARIO_ON_HEAD_CELL_OFF'
         }), (2, {
@@ -303,24 +308,24 @@
         """
 
         sar_config = collections.OrderedDict()
-        list_of_countries = ['fcc', 'jp']
+        list_of_countries = ['fcc', 'jp', 'ca']
         try:
             sar_config['country'] = next(country
                                          for country in list_of_countries
-                                         if country in sar_line)
+                                         if country in sar_line.split('=')[0])
         except:
             sar_config['country'] = 'row'
 
         list_of_sar_states = ['grip', 'bt', 'hotspot']
         try:
             sar_config['state'] = next(state for state in list_of_sar_states
-                                       if state in sar_line)
+                                       if state in sar_line.split('=')[0])
         except:
             sar_config['state'] = 'head'
 
         list_of_bands = ['2g', '5g', '6g']
         sar_config['band'] = next(band for band in list_of_bands
-                                  if band in sar_line)
+                                  if band in sar_line.split('=')[0])
 
         sar_config['rsdb'] = 'rsdb' if 'rsdb' in sar_line else 'mimo'
         sar_config['airplane_mode'] = '_2=' in sar_line
@@ -328,9 +333,10 @@
         sar_powers = sar_line.split('=')[1].split(',')
         decoded_powers = []
         for sar_power in sar_powers:
+            # Note that core 0 and 1 are flipped in the NVRAM entries
             decoded_powers.append([
-                (int(sar_power[2:4], 16) & int('7f', 16)) / 4,
-                (int(sar_power[4:], 16) & int('7f', 16)) / 4
+                (int(sar_power[4:], 16) & int('7f', 16)) / 4,
+                (int(sar_power[2:4], 16) & int('7f', 16)) / 4
             ])
 
         return tuple(sar_config.values()), decoded_powers
@@ -353,6 +359,8 @@
             reg_domain = 'fcc'
         elif testcase_params['country_code'] == 'JP':
             reg_domain = 'jp'
+        elif testcase_params['country_code'] == 'CA':
+            reg_domain = 'ca'
         else:
             reg_domain = 'row'
         for band, channels in self.BAND_TO_CHANNEL_MAP.items():
@@ -385,6 +393,8 @@
             reg_domain = 'fcc'
         elif testcase_params['country_code'] == 'JP':
             reg_domain = 'jp'
+        elif testcase_params['country_code'] == 'CA':
+            reg_domain = 'ca'
         else:
             reg_domain = 'row'
         for band, channels in self.BAND_TO_CHANNEL_MAP.items():
@@ -597,19 +607,22 @@
                       str) and '6g' in result['testcase_params']['channel']:
             mode = 'HE' + str(result['testcase_params']['bandwidth'])
         else:
-            mode = 'VHT' + str(result['testcase_params']['bandwidth'])
+            mode = 'HE' + str(result['testcase_params']['bandwidth'])
         regulatory_power = result['wl_curpower']['regulatory_limits'][(mode, 0,
                                                                        2)]
-        if result['testcase_params']['sar_state'] == 0:
-            #get from wl_curpower
-            csv_powers = [30, 30]
-            nvram_powers = [30, 30]
-            sar_config = 'SAR DISABLED'
-        else:
-            sar_config, nvram_powers = self.get_sar_power_from_nvram(
-                result['testcase_params'])
+        board_power = result['wl_curpower']['board_limits'][(mode, str(0), 2)]
+        # try:
+        sar_config, nvram_powers = self.get_sar_power_from_nvram(
+            result['testcase_params'])
+        # except:
+        #     nvram_powers = [99, 99]
+        #     sar_config = 'SAR DISABLED'
+        try:
             csv_config, csv_powers = self.get_sar_power_from_csv(
                 result['testcase_params'])
+        except:
+            #get from wl_curpower
+            csv_powers = [99, 99]
         self.log.info("SAR state: {} ({})".format(
             result['testcase_params']['sar_state'],
             self.sar_state_mapping[result['testcase_params']['sar_state']],
@@ -618,12 +631,12 @@
             result['testcase_params']['country_code']))
         self.log.info('BRCM SAR Table: {}'.format(sar_config))
         expected_power = [
-            min([csv_powers[0], regulatory_power]) - 1.5,
-            min([csv_powers[1], regulatory_power]) - 1.5
+            min([csv_powers[0], regulatory_power, board_power]) - 1.5,
+            min([csv_powers[1], regulatory_power, board_power]) - 1.5
         ]
-        power_str = "NVRAM Powers: {}, CSV Powers: {}, Reg Powers: {}, Expected Powers: {}, Reported Powers: {}".format(
-            nvram_powers, csv_powers, [regulatory_power] * 2, expected_power,
-            result['tx_powers'])
+        power_str = "NVRAM Powers: {}, CSV Powers: {}, Reg Powers: {}, Board Power: {}, Expected Powers: {}, Reported Powers: {}".format(
+            nvram_powers, csv_powers, [regulatory_power] * 2,
+            [board_power] * 2, expected_power, result['tx_powers'])
         max_error = max([
             abs(expected_power[idx] - result['tx_powers'][idx])
             for idx in [0, 1]
@@ -668,6 +681,12 @@
                 bw=testcase_params['bandwidth'],
                 duration=testcase_params['ping_duration'] *
                 len(testcase_params['atten_range']) + self.TEST_TIMEOUT)
+        # Set sar state
+        if testcase_params['sar_state'] == -1:
+            self.dut.adb.shell('halutil -sar disable')
+        else:
+            self.dut.adb.shell('halutil -sar enable {}'.format(
+                testcase_params['sar_state']))
         # Run ping and sweep attenuation as needed
         self.log.info('Starting ping.')
         thread_future = wputils.get_ping_stats_nb(self.ping_server,
@@ -679,18 +698,15 @@
             # Set mcs
             if isinstance(testcase_params['channel'],
                           int) and testcase_params['channel'] < 13:
-                self.dut.adb.shell('wl 2g_rate -v 0x2 -b {}'.format(
+                self.dut.adb.shell('wl 2g_rate -e 0 -s 2 -b {}'.format(
                     testcase_params['bandwidth']))
             elif isinstance(testcase_params['channel'],
                             int) and testcase_params['channel'] > 13:
-                self.dut.adb.shell('wl 5g_rate -v 0x2 -b {}'.format(
+                self.dut.adb.shell('wl 5g_rate -e 0 -s 2 -b {}'.format(
                     testcase_params['bandwidth']))
             else:
                 self.dut.adb.shell('wl 6g_rate -e 0 -s 2 -b {}'.format(
                     testcase_params['bandwidth']))
-            # Set sar state
-            self.dut.adb.shell('halutil -sar enable {}'.format(
-                testcase_params['sar_state']))
             # Refresh link layer stats
             llstats_obj.update_stats()
             # Check sar state
@@ -703,11 +719,16 @@
                 last_est_out = self.dut.adb.shell(
                     "wl curpower | grep 'Last est. power'", ignore_status=True)
                 if "Last est. power" in last_est_out:
-                    per_chain_powers = last_est_out.split(
-                        ':')[1].strip().split('  ')
-                    per_chain_powers = [
-                        float(power) for power in per_chain_powers
-                    ]
+                    try:
+                        per_chain_powers = last_est_out.split(
+                            ':')[1].strip().split('  ')
+                        per_chain_powers = [
+                            float(power) for power in per_chain_powers
+                        ]
+                    except:
+                        per_chain_powers = [0, 0]
+                        self.log.warning(
+                            'Could not parse output: {}'.format(last_est_out))
                     self.log.info(
                         'Current Tx Powers = {}'.format(per_chain_powers))
                     if per_chain_powers[0] > 0:
@@ -786,8 +807,11 @@
             self.access_point.set_region(self.testbed_params['DFS_region'])
         else:
             self.access_point.set_region(self.testbed_params['default_region'])
-        self.access_point.set_channel(band, testcase_params['channel'])
-        self.access_point.set_bandwidth(band, testcase_params['mode'])
+        self.access_point.set_channel_and_bandwidth(band,
+                                                    testcase_params['channel'],
+                                                    testcase_params['mode'])
+        #self.access_point.set_channel(band, testcase_params['channel'])
+        #self.access_point.set_bandwidth(band, testcase_params['mode'])
         if 'low' in testcase_params['ap_power']:
             self.log.info('Setting low AP power.')
             self.access_point.set_power(
@@ -825,9 +849,17 @@
         if self.testbed_params.get('txbf_off', False):
             wputils.disable_beamforming(self.dut)
         wutils.set_wifi_country_code(self.dut, testcase_params['country_code'])
+        current_country = self.dut.adb.shell('wl country')
+        self.log.info('Current country code: {}'.format(current_country))
+        if testcase_params['country_code'] not in current_country:
+            asserts.fail('Country code not correct.')
+        chan_list = self.dut.adb.shell('wl chan_info_list')
+        if str(testcase_params['channel']) not in chan_list:
+            asserts.skip('Channel {} not supported in {}'.format(
+                testcase_params['channel'], testcase_params['country_code']))
         wutils.wifi_connect(self.dut,
                             testcase_params['test_network'],
-                            num_of_tries=1,
+                            num_of_tries=5,
                             check_connectivity=True)
         self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
 
@@ -925,3 +957,23 @@
                     partial(self._test_ping, testcase_params))
             test_cases.append(testcase_name)
         return test_cases
+
+
+class WifiTxPowerCheck_BasicSAR_Test(WifiTxPowerCheckTest):
+
+    def __init__(self, controllers):
+        base_test.BaseTestClass.__init__(self, controllers)
+        self.testcase_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_case())
+        self.testclass_metric_logger = (
+            BlackboxMappedMetricLogger.for_test_class())
+        self.publish_testcase_metrics = True
+        self.tests = self.generate_test_cases(
+            ap_power='standard',
+            channels=[6, 36, 52, 100, 149, '6g37'],
+            modes=['bw20', 'bw160'],
+            test_types=[
+                'test_tx_power',
+            ],
+            country_codes=['US', 'GB', 'JP', 'CA'],
+            sar_states=[-1, 0, 1, 2, 3, 4])
diff --git a/acts_tests/tests/google/wifi/WifiWakeTest.py b/acts_tests/tests/google/wifi/WifiWakeTest.py
index 99870f6..ddd5899 100644
--- a/acts_tests/tests/google/wifi/WifiWakeTest.py
+++ b/acts_tests/tests/google/wifi/WifiWakeTest.py
@@ -400,6 +400,10 @@
         self.ap_b_off()
         wutils.wait_for_disconnect(self.dut, DISCONNECT_TIMEOUT_SEC)
         self.log.info("Wifi Disconnected")
+
+        if self.dut.model in self.user_params["google_pixel_watch_models"]:
+            wutils.disable_wear_wifimediator(self.dut, True)
+
         self.do_location_scan(2)
         time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 1.2)
         wutils.wifi_toggle_state(self.dut, new_state=False)
@@ -411,6 +415,9 @@
         self.ap_a_atten.set_atten(30)
         self.ap_b_atten.set_atten(0)
 
+        if self.dut.model in self.user_params["google_pixel_watch_models"]:
+            wutils.disable_wear_wifimediator(self.dut, False)
+
         self.do_location_scan(
             SCANS_REQUIRED_TO_FIND_SSID, self.ap_b[wutils.WifiEnums.SSID_KEY])
         time.sleep(WIFI_TOGGLE_DELAY_SEC)
diff --git a/acts_tests/tests/google/wifi/WifiWpa2PersonalTest.py b/acts_tests/tests/google/wifi/WifiWpa2PersonalTest.py
index 65b8235..062e66c 100644
--- a/acts_tests/tests/google/wifi/WifiWpa2PersonalTest.py
+++ b/acts_tests/tests/google/wifi/WifiWpa2PersonalTest.py
@@ -43,7 +43,7 @@
     super().setup_class()
     self.dut = self.android_devices[0]
     req_params = ["roaming_attn"]
-    opt_params = ["pixel_models", "cnss_diag_file"]
+    opt_params = []
     self.unpack_userparams(req_params, opt_params)
 
   def setup_test(self):
diff --git a/acts_tests/tests/google/wifi/WifiWpa3AutoUpdateTest.py b/acts_tests/tests/google/wifi/WifiWpa3AutoUpdateTest.py
index 6ce5e35..ea4ba50 100644
--- a/acts_tests/tests/google/wifi/WifiWpa3AutoUpdateTest.py
+++ b/acts_tests/tests/google/wifi/WifiWpa3AutoUpdateTest.py
@@ -14,7 +14,6 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import re
 from acts.libs.ota import ota_updater
 import acts.signals as signals
 from acts.test_decorators import test_tracker_info
@@ -45,14 +44,13 @@
 
     def __init__(self, configs):
         super().__init__(configs)
-        self.tests = (
-            "test_check_wpa3_wifi_state_after_au",
-            "test_verify_wpa3_networks_after_au",
-            "test_wpa3_configstore_after_au",
-            "test_all_wpa3_networks_connectable_after_au",
-            "test_check_wpa3_wifi_toggling_after_au",
-            "test_wpa3_connection_to_new_networks",
-            "test_reset_wpa3_wifi_after_au")
+        self.tests = ("test_check_wpa3_wifi_state_after_au",
+                      "test_verify_wpa3_networks_after_au",
+                      "test_wpa3_configstore_after_au",
+                      "test_all_wpa3_networks_connectable_after_au",
+                      "test_check_wpa3_wifi_toggling_after_au",
+                      "test_wpa3_connection_to_new_networks",
+                      "test_reset_wpa3_wifi_after_au")
 
     def setup_class(self):
         super(WifiBaseTest, self).setup_class()
@@ -63,15 +61,16 @@
         wutils.wifi_toggle_state(self.dut, True)
 
         # configure APs
-        req_params = ["ec2_ca_cert", "ec2_client_cert", "ec2_client_key", "rsa3072_ca_cert",
-                     "rsa3072_client_cert", "rsa3072_client_key", "wpa3_ec2_network",
-                     "wpa3_rsa3072_network", "rsa2048_client_cert", "rsa2048_client_key",
-                     "rsa3072_client_cert_expired", "rsa3072_client_cert_corrupted",
-                     "rsa3072_client_cert_unsigned", "rsa3072_client_key_unsigned",
-                     "wpa3_sae_gcmp_128", "wpa3_sae_gcmp_256","owe_networks", "sae_networks"]
-        self.unpack_userparams(
-                               req_param_names=req_params
-                               )
+        req_params = [
+            "ec2_ca_cert", "ec2_client_cert", "ec2_client_key",
+            "rsa3072_ca_cert", "rsa3072_client_cert", "rsa3072_client_key",
+            "wpa3_ec2_network", "wpa3_rsa3072_network", "rsa2048_client_cert",
+            "rsa2048_client_key", "rsa3072_client_cert_expired",
+            "rsa3072_client_cert_corrupted", "rsa3072_client_cert_unsigned",
+            "rsa3072_client_key_unsigned", "wpa3_sae_gcmp_128",
+            "wpa3_sae_gcmp_256", "owe_networks", "sae_networks"
+        ]
+        self.unpack_userparams(req_param_names=req_params)
         self.owe_2g = self.owe_networks[0]["2g"]
         self.owe_5g = self.owe_networks[0]["5g"]
         self.wpa3_personal_2g = self.sae_networks[0]["2g"]
@@ -86,13 +85,15 @@
             WifiEnums.SECURITY: WPA3_SECURITY,
             "identity": self.wpa3_rsa3072_network["identity"],
             "domain_suffix_match": self.wpa3_rsa3072_network["domain"]
-            }
+        }
 
         # saved & connected networks, network suggestions
         # and new networks
         self.saved_networks = [self.wpa3_sae_gcmp_256]
         self.network_suggestions = [self.owe_2g]
-        self.connected_networks = [self.config_rsa3072_tls, self.wpa3_personal_5g]
+        self.connected_networks = [
+            self.config_rsa3072_tls, self.wpa3_personal_5g
+        ]
         self.new_networks = [self.wpa3_personal_2g]
         # add pre ota upgrade configuration
         self.wifi_config_list = []
@@ -149,9 +150,13 @@
             self.dut, reconnect_to[SSID])
 
         if reconnect_to[SSID] == self.connected_networks[0][SSID]:
-            wutils.wifi_connect(self.dut, self.connected_networks[0], num_of_tries=6)
+            wutils.wifi_connect(self.dut,
+                                self.connected_networks[0],
+                                num_of_tries=6)
         else:
-            wutils.wifi_connect(self.dut, self.connected_networks[1], num_of_tries=6)
+            wutils.wifi_connect(self.dut,
+                                self.connected_networks[1],
+                                num_of_tries=6)
         connect_data = self.dut.droid.wifiGetConnectionInfo()
         connect_ssid = connect_data[SSID]
         self.log.info("Expected SSID = %s" % reconnect_to[SSID])
@@ -190,8 +195,7 @@
                1. Connect to previously added wpa3 network using network id.
         """
         network = self.wifi_config_list[0]
-        if not wutils.connect_to_wifi_network_with_id(self.dut,
-                                                      network[NETID],
+        if not wutils.connect_to_wifi_network_with_id(self.dut, network[NETID],
                                                       network[SSID]):
             raise signals.TestFailure("Failed to connect to %s after OTA" %
                                       network[SSID])
diff --git a/acts_tests/tests/google/wifi/WifiWpaPersonalTest.py b/acts_tests/tests/google/wifi/WifiWpaPersonalTest.py
index ad8856a..8d1bade 100644
--- a/acts_tests/tests/google/wifi/WifiWpaPersonalTest.py
+++ b/acts_tests/tests/google/wifi/WifiWpaPersonalTest.py
@@ -45,7 +45,7 @@
       self.configure_openwrt_ap_and_start(wpa1_network=True)
 
     req_params = ["OpenWrtAP", "roaming_attn"]
-    opt_params = ["pixel_models", "cnss_diag_file"]
+    opt_params = []
     self.unpack_userparams(req_params, opt_params)
     self.wpa_psk_2g = self.wpa1_networks[0]["2g"]
     self.wpa_psk_5g = self.wpa1_networks[0]["5g"]
diff --git a/acts_tests/tests/google/wifi/aware/functional/AttachTest.py b/acts_tests/tests/google/wifi/aware/functional/AttachTest.py
index eee0f9d..8b16dc7 100644
--- a/acts_tests/tests/google/wifi/aware/functional/AttachTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/AttachTest.py
@@ -166,3 +166,18 @@
         # try enabling Aware again (attach)
         dut.droid.wifiAwareAttach()
         autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+    @test_tracker_info(uuid="")
+    def test_attach_detach_attach_again(self):
+        """Validated there is no delay between Disable Aware and re-enable Aware
+        """
+        dut = self.android_devices[0]
+
+        # enable Aware (attach)
+        dut.droid.wifiAwareAttach()
+        autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+        dut.droid.wifiAwareDestroyAll()
+        # Restart Aware immediately
+        dut.droid.wifiAwareAttach()
+        autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED, timeout=1)
diff --git a/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py b/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py
index 39f009d..69dc42c 100644
--- a/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py
@@ -14,6 +14,7 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+import queue
 import string
 import time
 
@@ -32,6 +33,7 @@
     PAYLOAD_SIZE_MIN = 0
     PAYLOAD_SIZE_TYPICAL = 1
     PAYLOAD_SIZE_MAX = 2
+    EVENT_TIMEOUT = 3
 
     # message strings
     query_msg = "How are you doing? 你好嗎?"
@@ -1111,6 +1113,18 @@
             event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], s_to_p_msg,
             "Message on service %s from Subscriber to Publisher "
             "not received correctly" % session_name["pub"][p_disc_id])
+        try:
+            event = p_dut.ed.pop_event(autils.decorate_event(aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+                                             p_disc_id), self.EVENT_TIMEOUT)
+            p_dut.log.info("re-transmit message received: "
+                           + event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING])
+            asserts.assert_equal(
+                event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], s_to_p_msg,
+                "Message on service %s from Subscriber to Publisher "
+                "not received correctly" % session_name["pub"][p_disc_id])
+        except queue.Empty:
+            p_dut.log.info("no re-transmit message")
+
         peer_id_on_pub = event["data"][aconsts.SESSION_CB_KEY_PEER_ID]
 
         # Message send from Publisher to Subscriber
@@ -1129,6 +1143,17 @@
             event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], p_to_s_msg,
             "Message on service %s from Publisher to Subscriber"
             "not received correctly" % session_name["sub"][s_disc_id])
+        try:
+            event = s_dut.ed.pop_event(autils.decorate_event(aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+                                                             s_disc_id), self.EVENT_TIMEOUT)
+            s_dut.log.info("re-transmit message received: "
+                           + event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING])
+            asserts.assert_equal(
+                event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], p_to_s_msg,
+                "Message on service %s from Publisher to Subscriber"
+                "not received correctly" % session_name["sub"][s_disc_id])
+        except queue.Empty:
+            s_dut.log.info("no re-transmit message")
 
     def run_multiple_concurrent_services_same_name_diff_ssi(self, type_x, type_y):
         """Validate same service name with multiple service specific info on publisher
diff --git a/acts_tests/tests/google/wifi/aware/functional/ProtocolsMultiCountryTest.py b/acts_tests/tests/google/wifi/aware/functional/ProtocolsMultiCountryTest.py
index f23ecb7..4cb82ff 100644
--- a/acts_tests/tests/google/wifi/aware/functional/ProtocolsMultiCountryTest.py
+++ b/acts_tests/tests/google/wifi/aware/functional/ProtocolsMultiCountryTest.py
@@ -17,7 +17,6 @@
 #import acts.test_utils.wifi.wifi_test_utils as wutils
 
 import time
-import random
 import re
 import logging
 import acts.controllers.packet_capture as packet_capture
@@ -33,13 +32,14 @@
 
 WifiEnums = wutils.WifiEnums
 
+
 class ProtocolsMultiCountryTest(AwareBaseTest):
     def __init__(self, controllers):
         AwareBaseTest.__init__(self, controllers)
         self.basetest_name = (
             "ping6_ib_unsolicited_passive_multicountry",
             "ping6_ib_solicited_active_multicountry",
-            )
+        )
 
         self.generate_tests()
 
@@ -54,15 +54,15 @@
         test_tracker_uuid = ""
 
         testcase_name = 'test_%s_%s' % (basetest_name, country)
-        test_case = test_tracker_info(uuid=test_tracker_uuid)(
-            lambda: base_test(country))
+        test_case = test_tracker_info(
+            uuid=test_tracker_uuid)(lambda: base_test(country))
         setattr(self, testcase_name, test_case)
         self.tests.append(testcase_name)
 
     def generate_tests(self):
         for country in self.user_params['wifi_country_code']:
-                for basetest_name in self.basetest_name:
-                    self.generate_testcase(basetest_name, country)
+            for basetest_name in self.basetest_name:
+                self.generate_testcase(basetest_name, country)
 
     def setup_class(self):
         super().setup_class()
@@ -81,7 +81,7 @@
         for ad in self.android_devices:
             ad.ed.clear_all_events()
 
-    def test_time(self,begin_time):
+    def test_time(self, begin_time):
         super(ProtocolsMultiCountryTest, self).setup_test()
         for ad in self.android_devices:
             ad.cat_adb_log(begin_time)
@@ -91,7 +91,6 @@
         for ad in self.android_devices:
             ad.adb.shell("cmd wifiaware reset")
 
-
     """Set of tests for Wi-Fi Aware data-paths: validating protocols running on
     top of a data-path"""
 
@@ -116,7 +115,6 @@
         out_nda0 = re.findall("Channel = (\d+)", out_nda01)
         return out_nda0
 
-
     def conf_packet_capture(self, band, channel):
         """Configure packet capture on necessary channels."""
         freq_to_chan = wutils.WifiEnums.freq_to_channel[int(channel)]
@@ -126,8 +124,8 @@
         if not result:
             logging.error("Failed to configure channel "
                           "for {} band".format(band))
-        self.pcap_procs = wutils.start_pcap(
-            self.packet_capture, band, self.test_name)
+        self.pcap_procs = wutils.start_pcap(self.packet_capture, band,
+                                            self.test_name)
         time.sleep(5)
 
     ########################################################################
@@ -153,7 +151,7 @@
              device_startup_offset=self.device_startup_offset)
         self.log.info("Interface names: P=%s, S=%s", p_aware_if, s_aware_if)
         self.log.info("Interface addresses (IPv6): P=%s, S=%s", p_ipv6, s_ipv6)
-        ndpfreg =int(self.get_ndp_freq(p_dut)[-1])
+        ndpfreg = int(self.get_ndp_freq(p_dut)[-1])
         ndp_channel = str(CHANNEL_MAP[ndpfreg])
         n = int(ndp_channel)
         if n in range(len(self.channel_list_2g)):
@@ -168,7 +166,7 @@
         if hasattr(self, 'packet_capture'):
             self.conf_packet_capture(ndp_band, ndpfreg)
 
-       # run ping6
+    # run ping6
         self.run_ping6(p_dut, s_ipv6)
         self.run_ping6(s_dut, p_ipv6)
 
@@ -202,7 +200,7 @@
              device_startup_offset=self.device_startup_offset)
         self.log.info("Interface names: P=%s, S=%s", p_aware_if, s_aware_if)
         self.log.info("Interface addresses (IPv6): P=%s, S=%s", p_ipv6, s_ipv6)
-        ndpfreg =int(self.get_ndp_freq(p_dut)[-1])
+        ndpfreg = int(self.get_ndp_freq(p_dut)[-1])
         ndp_channel = str(CHANNEL_MAP[ndpfreg])
         n = int(ndp_channel)
         if n in range(len(self.channel_list_2g)):
diff --git a/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py b/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py
index 871602b..9c44ca6 100644
--- a/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py
+++ b/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py
@@ -33,7 +33,7 @@
     # take some time
     WAIT_FOR_CLUSTER = 5
 
-    def start_discovery_session(self, dut, session_id, is_publish, dtype):
+    def start_discovery_session(self, dut, session_id, is_publish, dtype, instant_mode = None):
         """Start a discovery session
 
     Args:
@@ -41,6 +41,7 @@
       session_id: ID of the Aware session in which to start discovery
       is_publish: True for a publish session, False for subscribe session
       dtype: Type of the discovery session
+      instant_mode: set the channel to use instant communication mode.
 
     Returns:
       Discovery session started event.
@@ -48,6 +49,9 @@
         config = {}
         config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = dtype
         config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = "GoogleTestServiceXY"
+        if instant_mode is not None:
+            config[aconsts.DISCOVERY_KEY_INSTANT_COMMUNICATION_MODE] = instant_mode
+
 
         if is_publish:
             disc_id = dut.droid.wifiAwarePublish(session_id, config)
@@ -74,6 +78,7 @@
                               solicited/active.
       dw_24ghz: DW interval in the 2.4GHz band.
       dw_5ghz: DW interval in the 5GHz band.
+      num_iterations: number of the iterations.
       startup_offset: The start-up gap (in seconds) between the two devices
       timeout_period: Time period over which to measure synchronization
     """
@@ -160,6 +165,7 @@
                               solicited/active.
       dw_24ghz: DW interval in the 2.4GHz band.
       dw_5ghz: DW interval in the 5GHz band.
+      num_iterations: number of the iterations.
     """
         key = "%s_dw24_%d_dw5_%d" % ("unsolicited_passive"
                                      if do_unsolicited_passive else
@@ -237,13 +243,15 @@
         p_dut.droid.wifiAwareDestroyAll()
         s_dut.droid.wifiAwareDestroyAll()
 
-    def run_message_latency(self, results, dw_24ghz, dw_5ghz, num_iterations):
+    def run_message_latency(self, results, dw_24ghz, dw_5ghz, num_iterations, instant_mode=None):
         """Run the message tx latency test with the specified DW intervals.
 
     Args:
       results: Result array to be populated - will add results (not erase it)
       dw_24ghz: DW interval in the 2.4GHz band.
       dw_5ghz: DW interval in the 5GHz band.
+      num_iterations: number of the iterations.
+      instant_mode: set the band to use instant communication mode, 2G or 5G
     """
         key = "dw24_%d_dw5_%d" % (dw_24ghz, dw_5ghz)
         results[key] = {}
@@ -262,9 +270,9 @@
              p_dut,
              s_dut,
              p_config=autils.create_discovery_config(
-                 self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED),
+                 self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED, instant_mode=instant_mode),
              s_config=autils.create_discovery_config(
-                 self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE),
+                 self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE, instant_mode=instant_mode),
              device_startup_offset=self.device_startup_offset)
 
         latencies = []
@@ -330,6 +338,7 @@
       results: Result array to be populated - will add results (not erase it)
       dw_24ghz: DW interval in the 2.4GHz band.
       dw_5ghz: DW interval in the 5GHz band.
+      num_iterations: number of the iterations.
     """
         key_avail = "on_avail_dw24_%d_dw5_%d" % (dw_24ghz, dw_5ghz)
         key_link_props = "link_props_dw24_%d_dw5_%d" % (dw_24ghz, dw_5ghz)
@@ -435,21 +444,21 @@
         results[key_avail]["ndp_setup_failures"] = ndp_setup_failures
 
     def run_end_to_end_latency(self, results, dw_24ghz, dw_5ghz,
-                               num_iterations, startup_offset, include_setup):
+                               num_iterations, startup_offset, include_setup, instant_mode = None):
         """Measure the latency for end-to-end communication link setup:
     - Start Aware
     - Discovery
-    - Message from Sub -> Pub
-    - Message from Pub -> Sub
     - NDP setup
 
     Args:
       results: Result array to be populated - will add results (not erase it)
       dw_24ghz: DW interval in the 2.4GHz band.
       dw_5ghz: DW interval in the 5GHz band.
+      num_iterations: number of the iterations.
       startup_offset: The start-up gap (in seconds) between the two devices
       include_setup: True to include the cluster setup in the latency
                     measurements.
+      instant_mode: set the band to use instant communication mode, 2G or 5G
     """
         key = "dw24_%d_dw5_%d" % (dw_24ghz, dw_5ghz)
         results[key] = {}
@@ -492,11 +501,19 @@
 
                 # start publish
                 p_disc_id, p_disc_event = self.start_discovery_session(
-                    p_dut, p_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED)
+                    p_dut, p_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED, instant_mode)
 
                 # start subscribe
                 s_disc_id, s_session_event = self.start_discovery_session(
-                    s_dut, s_id, False, aconsts.SUBSCRIBE_TYPE_PASSIVE)
+                    s_dut, s_id, False, aconsts.SUBSCRIBE_TYPE_PASSIVE, instant_mode)
+
+                # create NDP
+
+                # Publisher: request network
+                p_req_key = autils.request_network(
+                    p_dut,
+                    p_dut.droid.wifiAwareCreateNetworkSpecifier(
+                        p_disc_id, None, None))
 
                 # wait for discovery (allow for failures here since running lots of
                 # samples and would like to get the partial data even in the presence of
@@ -516,82 +533,6 @@
                     failures = failures + 1
                     break
 
-                # message from Sub -> Pub
-                msg_s2p = "Message Subscriber -> Publisher #%d" % i
-                next_msg_id = self.get_next_msg_id()
-                s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub,
-                                                 next_msg_id, msg_s2p, 0)
-
-                # wait for Tx confirmation
-                try:
-                    s_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_SENT,
-                                       autils.EVENT_TIMEOUT)
-                except queue.Empty:
-                    s_dut.log.info("[Subscriber] Timed out while waiting for "
-                                   "SESSION_CB_ON_MESSAGE_SENT")
-                    failures = failures + 1
-                    break
-
-                # wait for Rx confirmation (and validate contents)
-                try:
-                    event = p_dut.ed.pop_event(
-                        aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
-                        autils.EVENT_TIMEOUT)
-                    peer_id_on_pub = event['data'][
-                        aconsts.SESSION_CB_KEY_PEER_ID]
-                    if (event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING]
-                            != msg_s2p):
-                        p_dut.log.info(
-                            "[Publisher] Corrupted input message - %s", event)
-                        failures = failures + 1
-                        break
-                except queue.Empty:
-                    p_dut.log.info("[Publisher] Timed out while waiting for "
-                                   "SESSION_CB_ON_MESSAGE_RECEIVED")
-                    failures = failures + 1
-                    break
-
-                # message from Pub -> Sub
-                msg_p2s = "Message Publisher -> Subscriber #%d" % i
-                next_msg_id = self.get_next_msg_id()
-                p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub,
-                                                 next_msg_id, msg_p2s, 0)
-
-                # wait for Tx confirmation
-                try:
-                    p_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_SENT,
-                                       autils.EVENT_TIMEOUT)
-                except queue.Empty:
-                    p_dut.log.info("[Publisher] Timed out while waiting for "
-                                   "SESSION_CB_ON_MESSAGE_SENT")
-                    failures = failures + 1
-                    break
-
-                # wait for Rx confirmation (and validate contents)
-                try:
-                    event = s_dut.ed.pop_event(
-                        aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
-                        autils.EVENT_TIMEOUT)
-                    if (event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING]
-                            != msg_p2s):
-                        s_dut.log.info(
-                            "[Subscriber] Corrupted input message - %s", event)
-                        failures = failures + 1
-                        break
-                except queue.Empty:
-                    s_dut.log.info("[Subscriber] Timed out while waiting for "
-                                   "SESSION_CB_ON_MESSAGE_RECEIVED")
-                    failures = failures + 1
-                    break
-
-                # create NDP
-
-                # Publisher: request network
-                p_req_key = autils.request_network(
-                    p_dut,
-                    p_dut.droid.wifiAwareCreateNetworkSpecifier(
-                        p_disc_id, peer_id_on_pub, None))
-
                 # Subscriber: request network
                 s_req_key = autils.request_network(
                     s_dut,
@@ -613,6 +554,8 @@
                          cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
                         (cconsts.NETWORK_CB_KEY_ID, s_req_key))
                 except:
+                    s_dut.log.info("[Subscriber] Timed out while waiting for "
+                                   "EVENT_NETWORK_CALLBACK")
                     failures = failures + 1
                     break
 
@@ -742,6 +685,34 @@
         asserts.explicit_pass(
             "test_message_latency_default_dws finished", extras=results)
 
+    def test_message_latency_default_dws_instant_mode_2g(self):
+        """Measure the send message latency with the default DW configuration. Test
+    performed on non-queued message transmission - i.e. waiting for confirmation
+    of reception (ACK) before sending the next message."""
+        results = {}
+        self.run_message_latency(
+            results=results,
+            dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+            dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+            num_iterations=100,
+            instant_mode="2G")
+        asserts.explicit_pass(
+            "test_message_latency_default_dws finished", extras=results)
+
+    def test_message_latency_default_dws_instant_mode_5g(self):
+        """Measure the send message latency with the default DW configuration. Test
+    performed on non-queued message transmission - i.e. waiting for confirmation
+    of reception (ACK) before sending the next message."""
+        results = {}
+        self.run_message_latency(
+            results=results,
+            dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+            dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+            num_iterations=100,
+            instant_mode="5G")
+        asserts.explicit_pass(
+            "test_message_latency_default_dws finished", extras=results)
+
     def test_message_latency_non_interactive_dws(self):
         """Measure the send message latency with the DW configuration for
     non-interactive mode. Test performed on non-queued message transmission -
@@ -787,8 +758,6 @@
         """Measure the latency for end-to-end communication link setup:
       - Start Aware
       - Discovery
-      - Message from Sub -> Pub
-      - Message from Pub -> Sub
       - NDP setup
     """
         results = {}
@@ -802,14 +771,48 @@
         asserts.explicit_pass(
             "test_end_to_end_latency_default_dws finished", extras=results)
 
+    def test_end_to_end_latency_default_dws_instant_mode_2g(self):
+        """Measure the latency for end-to-end communication link setup:
+      - Start Aware
+      - Discovery
+      - NDP setup
+    """
+        results = {}
+        self.run_end_to_end_latency(
+            results,
+            dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+            dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+            num_iterations=10,
+            startup_offset=0,
+            include_setup=True,
+            instant_mode="2G")
+        asserts.explicit_pass(
+            "test_end_to_end_latency_default_dws finished", extras=results)
+
+    def test_end_to_end_latency_default_dws_instant_mode_5g(self):
+        """Measure the latency for end-to-end communication link setup:
+      - Start Aware
+      - Discovery
+      - NDP setup
+    """
+        results = {}
+        self.run_end_to_end_latency(
+            results,
+            dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+            dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+            num_iterations=10,
+            startup_offset=0,
+            include_setup=True,
+            instant_mode="5G")
+        asserts.explicit_pass(
+            "test_end_to_end_latency_default_dws finished", extras=results)
+
     def test_end_to_end_latency_post_attach_default_dws(self):
         """Measure the latency for end-to-end communication link setup without
     the initial synchronization:
       - Start Aware & synchronize initially
       - Loop:
         - Discovery
-        - Message from Sub -> Pub
-        - Message from Pub -> Sub
         - NDP setup
     """
         results = {}
@@ -823,3 +826,59 @@
         asserts.explicit_pass(
             "test_end_to_end_latency_post_attach_default_dws finished",
             extras=results)
+
+    def test_end_to_end_latency_post_attach_default_dws_instant_mode_2g(self):
+        """Measure the latency for end-to-end communication link setup without
+    the initial synchronization:
+        - Start Aware & synchronize initially
+        - Loop:
+        - Discovery
+        - NDP setup
+    """
+        asserts.skip_if(not self.android_devices[0].droid.isSdkAtLeastT(),
+                        "instant communication mode is only supported on T+")
+        asserts.skip_if(not (self.android_devices[0].aware_capabilities[aconsts
+                             .CAP_SUPPORTED_INSTANT_COMMUNICATION_MODE]
+                             and self.android_devices[0].aware_capabilities[aconsts
+                             .CAP_SUPPORTED_INSTANT_COMMUNICATION_MODE]),
+                        "Device doesn't support instant communication mode")
+        results = {}
+        self.run_end_to_end_latency(
+            results,
+            dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+            dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+            num_iterations=10,
+            startup_offset=0,
+            include_setup=False,
+            instant_mode="2G")
+        asserts.explicit_pass(
+            "test_end_to_end_latency_post_attach_default_dws_instant_mode finished",
+            extras=results)
+
+    def test_end_to_end_latency_post_attach_default_dws_instant_mode_5g(self):
+        """Measure the latency for end-to-end communication link setup without
+    the initial synchronization:
+        - Start Aware & synchronize initially
+        - Loop:
+        - Discovery
+        - NDP setup
+    """
+        asserts.skip_if(not self.android_devices[0].droid.isSdkAtLeastT(),
+                        "instant communication mode is only supported on T+")
+        asserts.skip_if(not (self.android_devices[0].aware_capabilities[aconsts
+                             .CAP_SUPPORTED_INSTANT_COMMUNICATION_MODE]
+                             and self.android_devices[0].aware_capabilities[aconsts
+                             .CAP_SUPPORTED_INSTANT_COMMUNICATION_MODE]),
+                        "Device doesn't support instant communication mode")
+        results = {}
+        self.run_end_to_end_latency(
+            results,
+            dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+            dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+            num_iterations=10,
+            startup_offset=0,
+            include_setup=False,
+            instant_mode="5G")
+        asserts.explicit_pass(
+            "test_end_to_end_latency_post_attach_default_dws_instant_mode finished",
+            extras=results)
diff --git a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
index fa4f53f..54bbbda 100644
--- a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
+++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
@@ -17,7 +17,6 @@
 import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
 import acts.utils
 import time
-import re
 
 from acts import asserts
 from acts import utils
@@ -42,6 +41,7 @@
     * At least two Android devices
     * An access point as sniffer
     """
+
     def __init__(self, controllers):
         WifiP2pBaseTest.__init__(self, controllers)
 
@@ -81,6 +81,7 @@
         wutils.verify_mac_not_found_in_pcap(self.dut2, self.dut2_mac, packets)
 
     """Test Cases"""
+
     @test_tracker_info(uuid="d04e62dc-e1ef-4cea-86e6-39f0dd08fb6b")
     def test_p2p_discovery_sniffer(self):
         """Verify the p2p discovery functionality
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py
index 1051fc4..84ad88e 100644
--- a/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py
@@ -339,19 +339,6 @@
 
     #############################################################################
 
-    @test_tracker_info(uuid="9e4e7ab4-2254-498c-9788-21e15ed9a370")
-    def test_rtt_oob_discovery_one_way(self):
-        """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
-        to communicate the MAC addresses to the peer. Test one-direction RTT only.
-        Functionality test: Only evaluate success rate.
-        """
-        rtt_results = self.run_rtt_oob_discovery_set(
-            do_both_directions=False,
-            iter_count=self.NUM_ITER,
-            time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
-            time_between_roles=self.TIME_BETWEEN_ROLES)
-        self.verify_results(rtt_results)
-
     @test_tracker_info(uuid="22edba77-eeb2-43ee-875a-84437550ad84")
     def test_rtt_oob_discovery_both_ways(self):
         """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
@@ -393,19 +380,6 @@
             time_between_roles=self.TIME_BETWEEN_ROLES)
         self.verify_results(rtt_results1, rtt_results2)
 
-    @test_tracker_info(uuid="3a1d19a2-7241-49e0-aaf2-0a1da4c29783")
-    def test_rtt_oob_discovery_one_way_with_accuracy_evaluation(self):
-        """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
-        to communicate the MAC addresses to the peer. Test one-direction RTT only.
-        Performance test: evaluate success rate and accuracy.
-        """
-        rtt_results = self.run_rtt_oob_discovery_set(
-            do_both_directions=False,
-            iter_count=self.NUM_ITER,
-            time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
-            time_between_roles=self.TIME_BETWEEN_ROLES)
-        self.verify_results(rtt_results, accuracy_evaluation=True)
-
     @test_tracker_info(uuid="82f954a5-c0ec-4bc6-8940-3b72199328ac")
     def test_rtt_oob_discovery_both_ways_with_accuracy_evaluation(self):
         """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
diff --git a/acts_tests/tests/google/wifi/wifi6/WifiEnterprise11axTest.py b/acts_tests/tests/google/wifi/wifi6/WifiEnterprise11axTest.py
index 7f0f121..94313b2 100644
--- a/acts_tests/tests/google/wifi/wifi6/WifiEnterprise11axTest.py
+++ b/acts_tests/tests/google/wifi/wifi6/WifiEnterprise11axTest.py
@@ -15,7 +15,7 @@
 
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiEnterpriseTest import WifiEnterpriseTest
+from ..WifiEnterpriseTest import WifiEnterpriseTest
 
 WifiEnums = wutils.WifiEnums
 # EAP Macros
diff --git a/acts_tests/tests/google/wifi/wifi6/WifiEnterpriseRoaming11axTest.py b/acts_tests/tests/google/wifi/wifi6/WifiEnterpriseRoaming11axTest.py
index ec70da9..781e5af 100644
--- a/acts_tests/tests/google/wifi/wifi6/WifiEnterpriseRoaming11axTest.py
+++ b/acts_tests/tests/google/wifi/wifi6/WifiEnterpriseRoaming11axTest.py
@@ -15,7 +15,7 @@
 
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiEnterpriseRoamingTest import WifiEnterpriseRoamingTest
+from ..WifiEnterpriseRoamingTest import WifiEnterpriseRoamingTest
 
 WifiEnums = wutils.WifiEnums
 # EAP Macros
diff --git a/acts_tests/tests/google/wifi/wifi6/WifiManager11axTest.py b/acts_tests/tests/google/wifi/wifi6/WifiManager11axTest.py
index d2fb981..fd906a0 100644
--- a/acts_tests/tests/google/wifi/wifi6/WifiManager11axTest.py
+++ b/acts_tests/tests/google/wifi/wifi6/WifiManager11axTest.py
@@ -14,7 +14,7 @@
 #   limitations under the License.
 
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiManagerTest import WifiManagerTest
+from ..WifiManagerTest import WifiManagerTest
 
 
 class WifiManager11axTest(WifiManagerTest):
diff --git a/acts_tests/tests/google/wifi/wifi6/WifiNetworkSuggestion11axTest.py b/acts_tests/tests/google/wifi/wifi6/WifiNetworkSuggestion11axTest.py
index 2c45e8c..ab08cce 100644
--- a/acts_tests/tests/google/wifi/wifi6/WifiNetworkSuggestion11axTest.py
+++ b/acts_tests/tests/google/wifi/wifi6/WifiNetworkSuggestion11axTest.py
@@ -15,7 +15,7 @@
 
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiNetworkSuggestionTest import WifiNetworkSuggestionTest
+from ..WifiNetworkSuggestionTest import WifiNetworkSuggestionTest
 
 WifiEnums = wutils.WifiEnums
 # EAP Macros
diff --git a/acts_tests/tests/google/wifi/wifi6/WifiPno11axTest.py b/acts_tests/tests/google/wifi/wifi6/WifiPno11axTest.py
index 7509d28..63b43ef 100644
--- a/acts_tests/tests/google/wifi/wifi6/WifiPno11axTest.py
+++ b/acts_tests/tests/google/wifi/wifi6/WifiPno11axTest.py
@@ -15,7 +15,7 @@
 
 import time
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiPnoTest import WifiPnoTest
+from ..WifiPnoTest import WifiPnoTest
 
 MAX_ATTN = 95
 
diff --git a/acts_tests/tests/google/wifi/wifi6/WifiSoftApAcs11axTest.py b/acts_tests/tests/google/wifi/wifi6/WifiSoftApAcs11axTest.py
index 0155be4..1a4a2c8 100644
--- a/acts_tests/tests/google/wifi/wifi6/WifiSoftApAcs11axTest.py
+++ b/acts_tests/tests/google/wifi/wifi6/WifiSoftApAcs11axTest.py
@@ -17,7 +17,7 @@
 from acts.controllers.ap_lib import hostapd_constants
 import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiSoftApAcsTest import WifiSoftApAcsTest
+from ..WifiSoftApAcsTest import WifiSoftApAcsTest
 
 
 class WifiSoftApAcs11axTest(WifiSoftApAcsTest):
diff --git a/acts_tests/tests/google/wifi/wifi6/WifiStaApConcurrency11axTest.py b/acts_tests/tests/google/wifi/wifi6/WifiStaApConcurrency11axTest.py
index bdb9dc1..88b1e90 100644
--- a/acts_tests/tests/google/wifi/wifi6/WifiStaApConcurrency11axTest.py
+++ b/acts_tests/tests/google/wifi/wifi6/WifiStaApConcurrency11axTest.py
@@ -17,7 +17,7 @@
 import acts.signals as signals
 import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiStaApConcurrencyTest import WifiStaApConcurrencyTest
+from ..WifiStaApConcurrencyTest import WifiStaApConcurrencyTest
 
 
 class WifiStaApConcurrency11axTest(WifiStaApConcurrencyTest):
diff --git a/acts_tests/tests/google/wifi/wifi6/WifiStaConcurrencyNetworkRequest11axTest.py b/acts_tests/tests/google/wifi/wifi6/WifiStaConcurrencyNetworkRequest11axTest.py
index 215a6e6..8dd76627b 100644
--- a/acts_tests/tests/google/wifi/wifi6/WifiStaConcurrencyNetworkRequest11axTest.py
+++ b/acts_tests/tests/google/wifi/wifi6/WifiStaConcurrencyNetworkRequest11axTest.py
@@ -16,7 +16,7 @@
 import acts.utils as utils
 import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiStaConcurrencyNetworkRequestTest import WifiStaConcurrencyNetworkRequestTest
+from ..WifiStaConcurrencyNetworkRequestTest import WifiStaConcurrencyNetworkRequestTest
 
 
 class WifiStaConcurrencyNetworkRequest11axTest(
diff --git a/acts_tests/tests/google/wifi/wifi6/WifiWpa311axTest.py b/acts_tests/tests/google/wifi/wifi6/WifiWpa311axTest.py
index 57ab366..cfe7987 100644
--- a/acts_tests/tests/google/wifi/wifi6/WifiWpa311axTest.py
+++ b/acts_tests/tests/google/wifi/wifi6/WifiWpa311axTest.py
@@ -14,7 +14,7 @@
 #   limitations under the License.
 
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiWpa3OweTest import WifiWpa3OweTest
+from ..WifiWpa3OweTest import WifiWpa3OweTest
 
 
 class WifiWpa311axTest(WifiWpa3OweTest):
diff --git a/acts_tests/tests/google/wifi/wifi6e/WifiNetworkSelector6eTest.py b/acts_tests/tests/google/wifi/wifi6e/WifiNetworkSelector6eTest.py
index 4a0ecf3..fa51247 100644
--- a/acts_tests/tests/google/wifi/wifi6e/WifiNetworkSelector6eTest.py
+++ b/acts_tests/tests/google/wifi/wifi6e/WifiNetworkSelector6eTest.py
@@ -17,7 +17,7 @@
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiNetworkSelectorTest import WifiNetworkSelectorTest
+from ..WifiNetworkSelectorTest import WifiNetworkSelectorTest
 
 # WifiNetworkSelector imposes a 10 seconds gap between two selections
 NETWORK_SELECTION_TIME_GAP = 12
diff --git a/acts_tests/tests/google/wifi/wifi6e/WifiPno6eTest.py b/acts_tests/tests/google/wifi/wifi6e/WifiPno6eTest.py
index 0bfc3da..fbccc88 100644
--- a/acts_tests/tests/google/wifi/wifi6e/WifiPno6eTest.py
+++ b/acts_tests/tests/google/wifi/wifi6e/WifiPno6eTest.py
@@ -17,7 +17,7 @@
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiPnoTest import WifiPnoTest
+from ..WifiPnoTest import WifiPnoTest
 
 MAX_ATTN = 95
 
diff --git a/acts_tests/tests/google/wifi/wifi6e/WifiStaApConcurrency6eTest.py b/acts_tests/tests/google/wifi/wifi6e/WifiStaApConcurrency6eTest.py
index 15a3264..5ba6dec 100644
--- a/acts_tests/tests/google/wifi/wifi6e/WifiStaApConcurrency6eTest.py
+++ b/acts_tests/tests/google/wifi/wifi6e/WifiStaApConcurrency6eTest.py
@@ -18,7 +18,7 @@
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
 from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
-from WifiStaApConcurrencyTest import WifiStaApConcurrencyTest
+from ..WifiStaApConcurrencyTest import WifiStaApConcurrencyTest
 
 WifiEnums = wutils.WifiEnums
 WIFI_CONFIG_SOFTAP_BAND_2G = WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G
diff --git a/acts_tests/tests/google/wifi/wifi6e/WifiTeleCoex6eTest.py b/acts_tests/tests/google/wifi/wifi6e/WifiTeleCoex6eTest.py
index 066abdd..9a35f4e 100644
--- a/acts_tests/tests/google/wifi/wifi6e/WifiTeleCoex6eTest.py
+++ b/acts_tests/tests/google/wifi/wifi6e/WifiTeleCoex6eTest.py
@@ -16,7 +16,7 @@
 
 from acts.test_decorators import test_tracker_info
 from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
-from WifiTeleCoexTest import WifiTeleCoexTest
+from ..WifiTeleCoexTest import WifiTeleCoexTest
 
 
 class WifiTeleCoex6eTest(WifiTeleCoexTest):
diff --git a/tools/create_virtualenv.sh b/tools/create_virtualenv.sh
index ad4931d..51ea260 100755
--- a/tools/create_virtualenv.sh
+++ b/tools/create_virtualenv.sh
@@ -1,10 +1,9 @@
 #!/bin/bash
 
-python3 -m pip install virtualenv
+python3 -m pip show virtualenv > /dev/null
 
 if [ $? -ne 0 ]; then
-  echo "Virtualenv must be installed to run the upload tests. Run: " >&2
-  echo "    sudo python3 -m pip install virtualenv" >&2
+  echo "The package 'virtualenv' must be installed to run the upload tests." >&2
   exit 1
 fi
 
@@ -16,5 +15,5 @@
 cd $virtualenv/framework
 echo "installing acts in virtual env" >> "${virtualenv}.log"
 $virtualenv/bin/python3 setup.py develop >> "${virtualenv}.log" 2>&1
-cd -
+cd - > /dev/null
 echo "done" >> "${virtualenv}.log"
diff --git a/tools/yapf_checker.py b/tools/yapf_checker.py
index c6565f1..3f4c8cc 100755
--- a/tools/yapf_checker.py
+++ b/tools/yapf_checker.py
@@ -15,7 +15,6 @@
 #   limitations under the License.
 import logging
 import os
-import sys
 
 from acts.libs.proc import job