Merge "Add VoNR/EPSFB/VoLTE/CSFB call results to json"
diff --git a/acts/framework/acts/controllers/fuchsia_device.py b/acts/framework/acts/controllers/fuchsia_device.py
index 96bd26c..6e4419e 100644
--- a/acts/framework/acts/controllers/fuchsia_device.py
+++ b/acts/framework/acts/controllers/fuchsia_device.py
@@ -114,7 +114,9 @@
 FUCHSIA_REBOOT_TYPE_HARD = 'hard'
 
 FUCHSIA_DEFAULT_CONNECT_TIMEOUT = 60
-FUCHSIA_DEFAULT_COMMAND_TIMEOUT = 3600
+FUCHSIA_DEFAULT_COMMAND_TIMEOUT = 60
+
+FUCHSIA_DEFAULT_CLEAN_UP_COMMAND_TIMEOUT = 15
 
 FUCHSIA_COUNTRY_CODE_TIMEOUT = 15
 FUCHSIA_DEFAULT_COUNTRY_CODE_US = 'US'
@@ -229,7 +231,7 @@
             "take_bug_report_on_fail", False)
         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)
+            'country_code', FUCHSIA_DEFAULT_COUNTRY_CODE_US).upper()
         self._persistent_ssh_conn = None
 
         # Whether to use 'policy' or 'drivers' for WLAN connect/disconnect calls
@@ -475,7 +477,11 @@
                 cmd,
                 timeout=timeout,
                 skip_status_code_check=skip_status_code_check)
-            if not skip_status_code_check and result.stderr:
+
+            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))
@@ -604,30 +610,21 @@
                         timeout=FUCHSIA_RECONNECT_AFTER_REBOOT_TIME,
                         skip_status_code_check=True)
             else:
-                self.log.info('Initializing reboot of FuchsiaDevice (%s)'
-                              ' with SL4F.' % self.ip)
                 self.log.info('Calling SL4F reboot command.')
                 with utils.SuppressLogOutput():
-                    if self.log_process:
-                        self.log_process.stop()
                     self.hardware_power_statecontrol_lib.suspendReboot(
                         timeout=3)
-                    if self._persistent_ssh_conn:
-                        self._persistent_ssh_conn.close()
-                        self._persistent_ssh_conn = None
+                    self.clean_up_services()
         elif reboot_type == FUCHSIA_REBOOT_TYPE_HARD:
             self.log.info('Power cycling FuchsiaDevice (%s)' % self.ip)
             device_pdu, device_pdu_port = pdu.get_pdu_port_for_device(
                 self.device_pdu_config, testbed_pdus)
             with utils.SuppressLogOutput():
-                if self.log_process:
-                    self.log_process.stop()
-                if self._persistent_ssh_conn:
-                    self._persistent_ssh_conn.close()
-                    self._persistent_ssh_conn = None
+                self.clean_up_services()
             self.log.info('Killing power to FuchsiaDevice (%s)...' % self.ip)
             device_pdu.off(str(device_pdu_port))
-
+        else:
+            raise ValueError('Invalid reboot type: %s' % reboot_type)
         # Wait for unreachable
         self.log.info('Verifying device is unreachable.')
         timeout = time.time() + unreachable_timeout
@@ -682,7 +679,6 @@
         self.log.info(
             'Restarting log process and reinitiating SL4F on FuchsiaDevice %s'
             % self.ip)
-        self.log_process.start()
         self.start_services()
 
         # Verify SL4F is up.
@@ -860,15 +856,24 @@
         """Cleans up the FuchsiaDevice object, releases any resources it
         claimed, and restores saved networks is applicable. For reboots, use
         clean_up_services only.
+
+        Note: Any exceptions thrown in this method must be caught and handled,
+        ensuring that clean_up_services is run. Otherwise, the syslog listening
+        thread will never join and will leave tests hanging.
         """
-        # If and only if wlan is configured using the policy layer
+        # If and only if wlan is configured, and using the policy layer
         if self.association_mechanism == 'policy':
-            self.wlan_policy_controller._clean_up()
+            try:
+                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 FuchsiaDeviceError:
-            self.log.warning('Failed to run 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()
 
     def clean_up_services(self):
@@ -887,7 +892,10 @@
         })
 
         try:
-            response = requests.get(url=self.cleanup_address, data=data).json()
+            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)
@@ -1042,6 +1050,7 @@
         if self.ssh_config:
             # 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(
                     desired_country_code)
                 if response.get('error'):
@@ -1051,13 +1060,13 @@
                 end_time = time.time() + FUCHSIA_COUNTRY_CODE_TIMEOUT
                 while time.time() < end_time:
                     ascii_cc = self.wlan_lib.wlanGetCountry(0).get('result')
-                    str_cc = ''.join(chr(c) for c in ascii_cc)
-                    if str_cc == desired_country_code:
+                    # Convert ascii_cc to string, then compare
+                    if ascii_cc and (''.join(chr(c) for c in ascii_cc).upper()
+                                     == desired_country_code):
                         self.log.debug('Country code successfully set to %s.' %
                                        desired_country_code)
                         return
-                    self.log.debug(
-                        'Country code is still set to %s. Retrying.' % str_cc)
+                    self.log.debug('Country code not yet updated. Retrying.')
                     time.sleep(1)
                 raise FuchsiaDeviceError('Country code never updated to %s' %
                                          desired_country_code)
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 d45e9e1..46cdf9d 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
@@ -9475,7 +9475,7 @@
         try:
             radio_simulate_data = json.load(f)
         except Exception as e:
-            self.log.error("Exception error to load %s: %s", f, e)
+            ad.log.error("Exception error to load %s: %s", f, e)
             return False
 
     for item in radio_simulate_data:
@@ -10783,7 +10783,7 @@
             try:
                 get_value = datetime_list[5]
             except Exception as e:
-                self.log.error("Fail to get year from datetime: %s. " \
+                ad.log.error("Fail to get year from datetime: %s. " \
                                 "Exception error: %s", datetime_list
                                 , str(e))
                 raise signals.TestSkip("Fail to get year from datetime" \
diff --git a/acts_tests/tests/OWNERS b/acts_tests/tests/OWNERS
index 6f15a8d..7e2f8ea 100644
--- a/acts_tests/tests/OWNERS
+++ b/acts_tests/tests/OWNERS
@@ -11,6 +11,7 @@
 mrtyler@google.com
 codycaldwell@google.com
 chaoyangf@google.com
+wju@google.com
 
 # Pixel GTW
 jasonkmlu@google.com
diff --git a/acts_tests/tests/google/fuchsia/wlan/functional/WlanRebootTest.py b/acts_tests/tests/google/fuchsia/wlan/functional/WlanRebootTest.py
index 02b2350..59060bd 100644
--- a/acts_tests/tests/google/fuchsia/wlan/functional/WlanRebootTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/functional/WlanRebootTest.py
@@ -50,6 +50,7 @@
 IPV4_ONLY = {IPV4: True, IPV6: False}
 IPV6_ONLY = {IPV4: False, IPV6: True}
 INTERRUPTS = [True, False]
+DEFAULT_IPERF_TIMEOUT = 30
 
 DUT_NETWORK_CONNECTION_TIMEOUT = 60
 DUT_IP_ADDRESS_TIMEOUT = 15
@@ -365,7 +366,9 @@
             'Attempting to pass traffic from DUT to IPerf server (%s).' %
             iperf_server_ip_address)
         tx_file = iperf_client_on_dut.start(iperf_server_ip_address,
-                                            '-i 1 -t 3 -J', 'reboot_tx')
+                                            '-i 1 -t 3 -J',
+                                            'reboot_tx',
+                                            timeout=DEFAULT_IPERF_TIMEOUT)
         tx_results = iperf_server.IPerfResult(tx_file)
         if not tx_results.avg_receive_rate or tx_results.avg_receive_rate == 0:
             raise ConnectionError(
@@ -380,7 +383,9 @@
             'Attempting to pass traffic from IPerf server (%s) to DUT.' %
             iperf_server_ip_address)
         rx_file = iperf_client_on_dut.start(iperf_server_ip_address,
-                                            '-i 1 -t 3 -R -J', 'reboot_rx')
+                                            '-i 1 -t 3 -R -J',
+                                            'reboot_rx',
+                                            timeout=DEFAULT_IPERF_TIMEOUT)
         rx_results = iperf_server.IPerfResult(rx_file)
         if not rx_results.avg_receive_rate or rx_results.avg_receive_rate == 0:
             raise ConnectionError(
diff --git a/acts_tests/tests/google/fuchsia/wlan/performance/ChannelSweepTest.py b/acts_tests/tests/google/fuchsia/wlan/performance/ChannelSweepTest.py
index 91606d4..a674911 100644
--- a/acts_tests/tests/google/fuchsia/wlan/performance/ChannelSweepTest.py
+++ b/acts_tests/tests/google/fuchsia/wlan/performance/ChannelSweepTest.py
@@ -57,6 +57,7 @@
 
 DEFAULT_MIN_THROUGHPUT = 0
 DEFAULT_MAX_STD_DEV = 1
+DEFAULT_IPERF_TIMEOUT = 30
 
 DEFAULT_TIME_TO_WAIT_FOR_IP_ADDR = 30
 GRAPH_CIRCLE_SIZE = 10
@@ -334,13 +335,19 @@
                 'Running IPerf traffic from server (%s) to dut (%s).' %
                 (iperf_server_address, iperf_client_address))
             iperf_results_file = self.iperf_client.start(
-                iperf_server_address, '-i 1 -t 10 -R -J', 'channel_sweep_rx')
+                iperf_server_address,
+                '-i 1 -t 10 -R -J',
+                'channel_sweep_rx',
+                timeout=DEFAULT_IPERF_TIMEOUT)
         else:
             self.log.info(
                 'Running IPerf traffic from dut (%s) to server (%s).' %
                 (iperf_client_address, iperf_server_address))
             iperf_results_file = self.iperf_client.start(
-                iperf_server_address, '-i 1 -t 10 -J', 'channel_sweep_tx')
+                iperf_server_address,
+                '-i 1 -t 10 -J',
+                'channel_sweep_tx',
+                timeout=DEFAULT_IPERF_TIMEOUT)
         if iperf_results_file:
             iperf_results = IPerfResult(
                 iperf_results_file, reporting_speed_units=MEGABITS_PER_SECOND)
@@ -657,7 +664,8 @@
             base_message = (
                 'Actual throughput (on channel: %s, channel bandwidth: '
                 '%s, security: %s)' % (channel, channel_bandwidth, security))
-            if (tx_throughput < min_tx_throughput
+            if (not tx_throughput or not rx_throughput
+                    or tx_throughput < min_tx_throughput
                     or rx_throughput < min_rx_throughput):
                 asserts.fail('%s below the minimum threshold.' % base_message)
             asserts.explicit_pass('%s above the minimum threshold.' %