Merge "Enable Wifi verbose logging in wifi performance tests"
diff --git a/acts/framework/acts/controllers/fuchsia_device.py b/acts/framework/acts/controllers/fuchsia_device.py
index 411d0ae..c5c1ad0 100644
--- a/acts/framework/acts/controllers/fuchsia_device.py
+++ b/acts/framework/acts/controllers/fuchsia_device.py
@@ -45,6 +45,7 @@
 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.location.regulatory_region_lib import FuchsiaRegulatoryRegionLib
@@ -256,6 +257,10 @@
         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,
@@ -293,11 +298,11 @@
         self.wlan_lib = FuchsiaWlanLib(self.address, self.test_counter,
                                        self.client_id)
 
-        #Grab commands from FuchsiaWlanApPolicyLib
+        # Grab commands from FuchsiaWlanApPolicyLib
         self.wlan_ap_policy_lib = FuchsiaWlanApPolicyLib(
             self.address, self.test_counter, self.client_id)
 
-        #Grab commands from FuchsiaWlanPolicyLib
+        # Grab commands from FuchsiaWlanPolicyLib
         self.wlan_policy_lib = FuchsiaWlanPolicyLib(self.address,
                                                     self.test_counter,
                                                     self.client_id)
diff --git a/acts/framework/acts/controllers/fuchsia_lib/i2c_lib.py b/acts/framework/acts/controllers/fuchsia_lib/i2c_lib.py
new file mode 100644
index 0000000..739e6e8
--- /dev/null
+++ b/acts/framework/acts/controllers/fuchsia_lib/i2c_lib.py
@@ -0,0 +1,49 @@
+#!/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/monsoon.py b/acts/framework/acts/controllers/monsoon.py
index a514e99..2728b06 100644
--- a/acts/framework/acts/controllers/monsoon.py
+++ b/acts/framework/acts/controllers/monsoon.py
@@ -69,4 +69,5 @@
 
 def destroy(monsoons):
     for monsoon in monsoons:
-        monsoon.release_monsoon_connection()
+        if monsoon.is_allocated():
+            monsoon.release_monsoon_connection()
diff --git a/acts/framework/acts/controllers/monsoon_lib/api/hvpm/monsoon.py b/acts/framework/acts/controllers/monsoon_lib/api/hvpm/monsoon.py
index 1353677..064310f 100644
--- a/acts/framework/acts/controllers/monsoon_lib/api/hvpm/monsoon.py
+++ b/acts/framework/acts/controllers/monsoon_lib/api/hvpm/monsoon.py
@@ -49,6 +49,7 @@
         self.serial = serial
         self._mon = HVPM.Monsoon()
         self._mon.setup_usb(serial)
+        self._allocated = True
         if self._mon.Protocol.DEVICE is None:
             raise ValueError('HVPM Monsoon %s could not be found.' % serial)
 
@@ -138,6 +139,7 @@
         manager.shutdown()
 
         self._mon.setup_usb(self.serial)
+        self._allocated = True
         monsoon_data = MonsoonResult(aggregator.num_samples,
                                      aggregator.sum_currents, hz, voltage,
                                      output_path)
@@ -153,6 +155,10 @@
 
     def release_monsoon_connection(self):
         self._mon.closeDevice()
+        self._allocated = False
+
+    def is_allocated(self):
+        return self._allocated
 
     def establish_monsoon_connection(self):
         self._mon.setup_usb(self.serial)
diff --git a/acts/framework/acts/controllers/monsoon_lib/api/lvpm_stock/monsoon.py b/acts/framework/acts/controllers/monsoon_lib/api/lvpm_stock/monsoon.py
index fa668ed..632c2bb 100644
--- a/acts/framework/acts/controllers/monsoon_lib/api/lvpm_stock/monsoon.py
+++ b/acts/framework/acts/controllers/monsoon_lib/api/lvpm_stock/monsoon.py
@@ -40,6 +40,7 @@
     def __init__(self, serial, device=None):
         super().__init__()
         self._mon = MonsoonProxy(serialno=serial, device=device)
+        self._allocated = True
         self.serial = serial
 
     def set_voltage(self, voltage):
@@ -138,8 +139,13 @@
 
     def release_monsoon_connection(self):
         self._mon.release_dev_port()
+        self._allocated = False
+
+    def is_allocated(self):
+        return self._allocated
 
     def establish_monsoon_connection(self):
         self._mon.obtain_dev_port()
+        self._allocated = True
         # Makes sure the Monsoon is in the command-receiving state.
         self._mon.stop_data_collection()
diff --git a/acts/framework/acts/controllers/monsoon_lib/api/monsoon.py b/acts/framework/acts/controllers/monsoon_lib/api/monsoon.py
index a55fc8a..72f093d 100644
--- a/acts/framework/acts/controllers/monsoon_lib/api/monsoon.py
+++ b/acts/framework/acts/controllers/monsoon_lib/api/monsoon.py
@@ -152,6 +152,8 @@
                                    'seconds.' % time_limit_seconds)
             self._set_usb_passthrough_mode(expected_state)
             time.sleep(1)
+        self._log.info('Monsoon usbPassthroughMode is now "%s"',
+                       state)
 
         if expected_state in [PassthroughStates.ON]:
             self._on_reconnect()
@@ -261,10 +263,10 @@
             TimeoutError upon failure to reconnect over USB.
         """
         self._log.info('Reconnecting dut.')
-        # Wait for one second to ensure that the relay is ready, then
+        # Wait for two seconds to ensure that the device is ready, then
         # attempt to reconnect. If reconnect times out, reset the passthrough
         # state and try again.
-        time.sleep(1)
+        time.sleep(2)
         try:
             self.on_reconnect()
         except TimeoutError as err:
@@ -282,6 +284,10 @@
         """Reconnects the Monsoon Serial/USB connection."""
         raise NotImplementedError()
 
+    def is_allocated(self):
+        """Whether the resource is locked."""
+        raise NotImplementedError()
+
     def release_monsoon_connection(self):
         """Releases the underlying monsoon Serial or USB connection.
 
diff --git a/acts/framework/acts/test_utils/gnss/gnss_testlog_utils.py b/acts/framework/acts/test_utils/gnss/gnss_testlog_utils.py
index 5de1a17..0792da4 100644
--- a/acts/framework/acts/test_utils/gnss/gnss_testlog_utils.py
+++ b/acts/framework/acts/test_utils/gnss/gnss_testlog_utils.py
@@ -169,7 +169,7 @@
         Type, Pandas DataFrame.
     """
     # Get parsed dataframe list
-    parsed_data = lputil.parse_log_to_df(
+    parsed_data = parse_log_to_df(
         filename=filename,
         configs=CONFIG_GPSTTFFLOG,
     )
@@ -200,6 +200,8 @@
         Type, Pandas DataFrame.
       sv_info_df: GNSS SV info Data Frame.
         Type, Pandas DataFrame.
+      sv_stat_df: GNSS SV statistic Data Frame.
+        Type, Pandas DataFrame
       loc_info_df: Location Information Data Frame.
         Type, Pandas DataFrame.
         include Provider, Latitude, Longitude, Altitude, GNSSTime, Speed, Bearing
@@ -289,6 +291,8 @@
         Type, Pandas DataFrame.
       sv_info_df: GNSS SV info Data Frame.
         Type, Pandas DataFrame.
+      sv_stat_df: GNSS SV statistic Data Frame.
+        Type, Pandas DataFrame
       loc_info_df: Location Information Data Frame.
         Type, Pandas DataFrame.
         include Provider, Latitude, Longitude, Altitude, GNSSTime, Speed, Bearing
@@ -345,4 +349,27 @@
                                                '%Y/%m/%d-%H:%M:%S'),
         axis=1)
 
+    # Data Conversion
+    timestamp_df['logsize'] = timestamp_df['logsize'].astype(int)
+
+    sv_info_df['SV'] = sv_info_df['SV'].astype(int)
+    sv_info_df['CNo'] = sv_info_df['CNo'].astype(float)
+    sv_info_df['Elevation'] = sv_info_df['Elevation'].astype(float)
+    sv_info_df['Azimuth'] = sv_info_df['Azimuth'].astype(float)
+    sv_info_df['Frequency'] = sv_info_df['Frequency'].astype(float)
+
+    sv_stat_df['CurrentAvgTop4CNo'] = sv_stat_df['CurrentAvgTop4CNo'].astype(
+        float)
+    sv_stat_df['CurrentAvgCNo'] = sv_stat_df['CurrentAvgCNo'].astype(float)
+    sv_stat_df['HistoryAvgTop4CNo'] = sv_stat_df['HistoryAvgTop4CNo'].astype(
+        float)
+    sv_stat_df['HistoryAvgCNo'] = sv_stat_df['HistoryAvgCNo'].astype(float)
+    sv_stat_df['L5EngagingRate'] = sv_stat_df['L5EngagingRate'].astype(float)
+
+    loc_info_df['Latitude'] = loc_info_df['Latitude'].astype(float)
+    loc_info_df['Longitude'] = loc_info_df['Longitude'].astype(float)
+    loc_info_df['Altitude'] = loc_info_df['Altitude'].astype(float)
+    loc_info_df['Speed'] = loc_info_df['Speed'].astype(float)
+    loc_info_df['Bearing'] = loc_info_df['Bearing'].astype(float)
+
     return timestamp_df, sv_info_df, sv_stat_df, loc_info_df
diff --git a/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py b/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
index 69bb67b..0c1777f 100644
--- a/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
+++ b/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
@@ -248,7 +248,8 @@
                 postfix=build_postfix)
         if self.enable_radio_log_on:
             enable_radio_log_on(ad)
-        if "sdm" in ad.model or "msm" in ad.model or "kon" in ad.model:
+        list_of_models = ["sdm", "msm", "kon", "lit"]
+        if any(model in ad.model for model in list_of_models):
             phone_mode = "ssss"
             if hasattr(ad, "mtp_dsds"):
                 phone_mode = "dsds"
diff --git a/acts/framework/acts/test_utils/tel/tel_test_utils.py b/acts/framework/acts/test_utils/tel/tel_test_utils.py
index 85c17b4..ac4f491 100644
--- a/acts/framework/acts/test_utils/tel/tel_test_utils.py
+++ b/acts/framework/acts/test_utils/tel/tel_test_utils.py
@@ -4834,13 +4834,6 @@
 
 def get_cell_data_roaming_state_by_adb(ad):
     """Get Cell Data Roaming state. True for enabled, False for disabled"""
-    adb_str = {"1": True, "0": False}
-    out = ad.adb.shell("settings get global data_roaming")
-    return adb_str[out]
-
-
-def get_cell_data_roaming_state_by_adb(ad):
-    """Get Cell Data Roaming state. True for enabled, False for disabled"""
     state_mapping = {"1": True, "0": False}
     return state_mapping[ad.adb.shell("settings get global data_roaming")]
 
@@ -5886,8 +5879,8 @@
                           ad_rx,
                           phonenumber_tx,
                           text,
-                          allow_multi_part_long_sms=True,
-                          max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE):
+                          max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE,
+                          allow_multi_part_long_sms=True):
     """Wait for matching incoming SMS.
 
     Args:
@@ -6070,6 +6063,7 @@
                     ad_rx,
                     phonenumber_tx,
                     text,
+                    max_wait_time,
                     allow_multi_part_long_sms=True):
                 ad_rx.log.error("No matching received SMS of length %s.",
                                 length)
@@ -6245,6 +6239,7 @@
             except Empty:
                 ad_tx.log.warning("No %s or %s event.", EventMmsSentSuccess,
                                   EventMmsSentFailure)
+                return False
 
             if not wait_for_matching_mms(log, ad_rx, phonenumber_tx,
                                          message, max_wait_time):
@@ -6253,7 +6248,7 @@
             log.error("Exception error %s", e)
             raise
         finally:
-            ad_rx.droid.smsStopTrackingIncomingMmsMessage()
+            ad_rx.messaging_droid.smsStopTrackingIncomingMmsMessage()
             for ad in (ad_tx, ad_rx):
                 if toggle_enforce:
                     ad.send_keycode("BACK")
diff --git a/acts/framework/setup.py b/acts/framework/setup.py
index da4ed64..be158f2 100755
--- a/acts/framework/setup.py
+++ b/acts/framework/setup.py
@@ -30,7 +30,7 @@
     # b/148695846, b/148814743
     'mock==3.0.5',
     # b/157117302: python3.5 is not supported by NumPy 1.19+
-    'numpy==1.18.1',
+    'numpy<=1.18.1',
     # b/157117302: python3.5 is not supported by SciPy 1.5.0+ (Monsoon dependency)
     'scipy==1.4.1',
     'pyserial',
diff --git a/acts/tests/google/fuchsia/backlight/BacklightTest.py b/acts/tests/google/fuchsia/backlight/BacklightTest.py
new file mode 100644
index 0000000..9d15781
--- /dev/null
+++ b/acts/tests/google/fuchsia/backlight/BacklightTest.py
@@ -0,0 +1,172 @@
+#!/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.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/google/fuchsia/input/TouchTest.py b/acts/tests/google/fuchsia/input/TouchTest.py
new file mode 100644
index 0000000..7467139
--- /dev/null
+++ b/acts/tests/google/fuchsia/input/TouchTest.py
@@ -0,0 +1,68 @@
+#!/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/google/gnss/AGNSSPerformanceTest.py b/acts/tests/google/gnss/AGNSSPerformanceTest.py
index 881c010..57d4c35 100644
--- a/acts/tests/google/gnss/AGNSSPerformanceTest.py
+++ b/acts/tests/google/gnss/AGNSSPerformanceTest.py
@@ -141,13 +141,6 @@
 
         passed = True
 
-        self.ttf_metric.metric_value = \
-            results.get(contest.Contest.TTFF_KEY, None)
-        self.pos_error_metric.metric_value = \
-            results.get(contest.Contest.POS_ERROR_KEY, None)
-        self.sensitivity_metric.metric_value = \
-            results.get(contest.Contest.SENSITIVITY_KEY, None)
-
         with open(self.thresholds_file, 'r') as file:
 
             thresholds = json.load(file)
@@ -175,6 +168,14 @@
                                                  thresholds[key]['max']))
                     passed = False
 
+                # Save metric to Blackbox logger
+                if key == contest.Contest.TTFF_KEY:
+                    self.ttf_metric.metric_value = metric
+                elif key == contest.Contest.POS_ERROR_KEY:
+                    self.pos_error_metric.metric_value = metric
+                elif key == contest.Contest.SENSITIVITY_KEY:
+                    self.sensitivity_metric.metric_value = metric
+
         asserts.assert_true(
             passed, 'At least one of the metrics was not '
             'within the expected values.')
diff --git a/acts/tests/google/tel/live/TelLiveSmsTest.py b/acts/tests/google/tel/live/TelLiveSmsTest.py
index 229269d..2529aea 100644
--- a/acts/tests/google/tel/live/TelLiveSmsTest.py
+++ b/acts/tests/google/tel/live/TelLiveSmsTest.py
@@ -42,6 +42,8 @@
 from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify
 from acts.test_utils.tel.tel_test_utils import mms_receive_verify_after_call_hangup
 from acts.test_utils.tel.tel_test_utils import multithread_func
+from acts.test_utils.tel.tel_test_utils import set_preferred_mode_for_5g
+from acts.test_utils.tel.tel_test_utils import is_current_network_5g_nsa
 from acts.test_utils.tel.tel_test_utils import set_call_state_listen_level
 from acts.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit
 from acts.test_utils.tel.tel_test_utils import setup_sim
@@ -569,6 +571,49 @@
 
         return self._mms_test_mo(ads)
 
+    @test_tracker_info(uuid="f2c27463-2cc2-45eb-80f1-aea2047858d6")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_sms_mo_mt_5g_nsa(self):
+        """Test SMS basic function between two phone. Phones in 5g NSA.
+
+        Airplane mode is off.
+        Send SMS from PhoneA to PhoneB.
+        Verify received message on PhoneB is correct.
+
+        Returns:
+            True if success.
+            False if failed.
+        """
+        ads = self.android_devices
+
+        # Mode Pref
+        tasks = [(set_preferred_mode_for_5g, [ad])
+                 for ad in self.android_devices]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Failed to set preferred network mode.")
+            return False
+
+        # Attach 5g
+        tasks = [(is_current_network_5g_nsa, [ad])
+                 for ad in self.android_devices]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone not on 5G NSA before sending SMS.")
+            return False
+
+        if not self._sms_test_mo(ads):
+            self.log.error("Failed to send receive SMS over 5G NSA.")
+            return False
+
+        # Attach 5g
+        tasks = [(is_current_network_5g_nsa, [ad])
+                 for ad in self.android_devices]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone not on 5G NSA after sending SMS.")
+            return False
+
+        self.log.info("PASS - SMS test over 5G NSA validated")
+        return True
+
     @test_tracker_info(uuid="f2779e1e-7d09-43f0-8b5c-87eae5d146be")
     @TelephonyBaseTest.tel_test_wrap
     def test_mms_mt_general(self):
@@ -616,6 +661,7 @@
 
         return self._sms_test_mo(ads)
 
+
     @test_tracker_info(uuid="17fafc41-7e12-47ab-a4cc-fb9bd94e79b9")
     @TelephonyBaseTest.tel_test_wrap
     def test_sms_mt_2g(self):
@@ -3050,4 +3096,4 @@
             return False
         time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
 
-        return self._sms_in_collision_when_power_off_test(ads)
\ No newline at end of file
+        return self._sms_in_collision_when_power_off_test(ads)
diff --git a/acts/tests/google/tel/live/TelLiveVoiceTest.py b/acts/tests/google/tel/live/TelLiveVoiceTest.py
index 9edf26f..95adc5f 100644
--- a/acts/tests/google/tel/live/TelLiveVoiceTest.py
+++ b/acts/tests/google/tel/live/TelLiveVoiceTest.py
@@ -717,6 +717,55 @@
         return True
 
 
+    @test_tracker_info(uuid="6add9dd3-f90e-4a96-a4e7-469e44b5caed")
+    @TelephonyBaseTest.tel_test_wrap
+    def test_call_volte_to_3g_5g_nsa(self):
+        """ VoLTE to 3G call test
+
+        1. Make Sure PhoneA is in 5g NSA mode (with VoLTE).
+        2. Make Sure PhoneB is in 3G mode.
+        3. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneA.
+        4. Call from PhoneA to PhoneB, accept on PhoneB, hang up on PhoneB.
+        5. Verify both PhoneA gets attached back to 5g NSA
+
+        Raises:
+            TestFailure if not success.
+        """
+        ads = self.android_devices
+
+        # LTE attach
+        tasks = [(phone_setup_volte, (self.log, ads[0])),
+                 (phone_setup_voice_3g, (self.log, ads[1]))]
+        if not multithread_func(self.log, tasks):
+            self.log.error("Phone Failed to Set Up in VoLTE/3G.")
+            return False
+
+        # Mode Pref
+        set_preferred_mode_for_5g(ads[0])
+
+        # Attach 5g
+        if not is_current_network_5g_nsa(ads[0]):
+            ads[0].log.error("Phone not attached on 5G NSA before call.")
+            return False
+
+        # VoLTE to 3G
+        result = two_phone_call_short_seq(
+            self.log, ads[0], None, is_phone_in_call_volte, ads[1],
+            None, is_phone_in_call_3g, None,
+            WAIT_TIME_IN_CALL_FOR_IMS)
+        if not result:
+            self.log.error("Failure is VoLTE to 3G call during 5G NSA.")
+            return False
+
+        # Attach 5g
+        if not is_current_network_5g_nsa(ads[0]):
+            ads[0].log.error("Phone not attached on 5G NSA after call end.")
+            return False
+
+        self.log.info("PASS - VoLTE to 3G over 5G NSA validated")
+        return True
+
+
     def _call_epdg_to_epdg_wfc(self, ads, apm_mode, wfc_mode, wifi_ssid,
                                wifi_pwd):
         """ Test epdg<->epdg call functionality.
diff --git a/acts_tests/tests b/acts_tests/tests
deleted file mode 120000
index 2d3141a..0000000
--- a/acts_tests/tests
+++ /dev/null
@@ -1 +0,0 @@
-../acts/tests
\ No newline at end of file
diff --git a/acts_tests/tests/OWNERS b/acts_tests/tests/OWNERS
new file mode 120000
index 0000000..d2642b6
--- /dev/null
+++ b/acts_tests/tests/OWNERS
@@ -0,0 +1 @@
+../../acts/tests/OWNERS
\ No newline at end of file
diff --git a/acts_tests/tests/google/__init__.py b/acts_tests/tests/google/__init__.py
new file mode 120000
index 0000000..84b1b6b
--- /dev/null
+++ b/acts_tests/tests/google/__init__.py
@@ -0,0 +1 @@
+../../../acts/tests/google/__init__.py
\ No newline at end of file
diff --git a/acts_tests/tests/google/ble b/acts_tests/tests/google/ble
new file mode 120000
index 0000000..45b76eb
--- /dev/null
+++ b/acts_tests/tests/google/ble
@@ -0,0 +1 @@
+../../../acts/tests/google/ble
\ No newline at end of file
diff --git a/acts_tests/tests/google/bt b/acts_tests/tests/google/bt
new file mode 120000
index 0000000..11c9bbd
--- /dev/null
+++ b/acts_tests/tests/google/bt
@@ -0,0 +1 @@
+../../../acts/tests/google/bt
\ No newline at end of file
diff --git a/acts_tests/tests/google/coex b/acts_tests/tests/google/coex
new file mode 120000
index 0000000..1d4fb61
--- /dev/null
+++ b/acts_tests/tests/google/coex
@@ -0,0 +1 @@
+../../../acts/tests/google/coex
\ No newline at end of file
diff --git a/acts_tests/tests/google/experimental b/acts_tests/tests/google/experimental
new file mode 120000
index 0000000..a1cac1e
--- /dev/null
+++ b/acts_tests/tests/google/experimental
@@ -0,0 +1 @@
+../../../acts/tests/google/experimental
\ No newline at end of file
diff --git a/acts_tests/tests/google/fuchsia b/acts_tests/tests/google/fuchsia
new file mode 120000
index 0000000..de44fc7
--- /dev/null
+++ b/acts_tests/tests/google/fuchsia
@@ -0,0 +1 @@
+../../../acts/tests/google/fuchsia
\ No newline at end of file
diff --git a/acts_tests/tests/google/fugu b/acts_tests/tests/google/fugu
new file mode 120000
index 0000000..a8ddea3
--- /dev/null
+++ b/acts_tests/tests/google/fugu
@@ -0,0 +1 @@
+../../../acts/tests/google/fugu
\ No newline at end of file
diff --git a/acts_tests/tests/google/gnss b/acts_tests/tests/google/gnss
new file mode 120000
index 0000000..83eaca4
--- /dev/null
+++ b/acts_tests/tests/google/gnss
@@ -0,0 +1 @@
+../../../acts/tests/google/gnss
\ No newline at end of file
diff --git a/acts_tests/tests/google/native b/acts_tests/tests/google/native
new file mode 120000
index 0000000..0199848
--- /dev/null
+++ b/acts_tests/tests/google/native
@@ -0,0 +1 @@
+../../../acts/tests/google/native
\ No newline at end of file
diff --git a/acts_tests/tests/google/net b/acts_tests/tests/google/net
new file mode 120000
index 0000000..ff765d6
--- /dev/null
+++ b/acts_tests/tests/google/net
@@ -0,0 +1 @@
+../../../acts/tests/google/net
\ No newline at end of file
diff --git a/acts_tests/tests/google/nfc b/acts_tests/tests/google/nfc
new file mode 120000
index 0000000..5b5dbb6
--- /dev/null
+++ b/acts_tests/tests/google/nfc
@@ -0,0 +1 @@
+../../../acts/tests/google/nfc
\ No newline at end of file
diff --git a/acts_tests/tests/google/power b/acts_tests/tests/google/power
new file mode 120000
index 0000000..97c73e9
--- /dev/null
+++ b/acts_tests/tests/google/power
@@ -0,0 +1 @@
+../../../acts/tests/google/power
\ No newline at end of file
diff --git a/acts_tests/tests/google/tel b/acts_tests/tests/google/tel
new file mode 120000
index 0000000..9eddd2a
--- /dev/null
+++ b/acts_tests/tests/google/tel
@@ -0,0 +1 @@
+../../../acts/tests/google/tel
\ No newline at end of file
diff --git a/acts_tests/tests/google/usb b/acts_tests/tests/google/usb
new file mode 120000
index 0000000..a47b35e
--- /dev/null
+++ b/acts_tests/tests/google/usb
@@ -0,0 +1 @@
+../../../acts/tests/google/usb
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi b/acts_tests/tests/google/wifi
new file mode 120000
index 0000000..48192ea
--- /dev/null
+++ b/acts_tests/tests/google/wifi
@@ -0,0 +1 @@
+../../../acts/tests/google/wifi
\ No newline at end of file
diff --git a/acts_tests/tests/meta b/acts_tests/tests/meta
new file mode 120000
index 0000000..df59e28
--- /dev/null
+++ b/acts_tests/tests/meta
@@ -0,0 +1 @@
+../../acts/tests/meta
\ No newline at end of file
diff --git a/acts_tests/tests/sample b/acts_tests/tests/sample
new file mode 120000
index 0000000..7ef857f
--- /dev/null
+++ b/acts_tests/tests/sample
@@ -0,0 +1 @@
+../../acts/tests/sample
\ No newline at end of file