Merge "Update the judgement of sim_mode for the new projects."
diff --git a/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py b/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py
index 94bc4df..be3056a 100644
--- a/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py
+++ b/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py
@@ -17,6 +17,13 @@
 from acts.controllers.android_lib.tel import tel_utils
 from acts.controllers.cellular_lib import BaseCellularDut
 
+GET_BUILD_VERSION = 'getprop ro.build.version.release'
+
+NETWORK_TYPE_TO_BITMASK = {
+    BaseCellularDut.PreferredNetworkType.LTE_ONLY: '01000001000000000000',
+    BaseCellularDut.PreferredNetworkType.NR_LTE: '11000001000000000000',
+    BaseCellularDut.PreferredNetworkType.WCDMA_ONLY: '00000100001110000100',
+}
 
 class AndroidCellularDut(BaseCellularDut.BaseCellularDut):
     """ Android implementation of the cellular DUT class."""
@@ -72,6 +79,22 @@
         Args:
           type: an instance of class PreferredNetworkType
         """
+
+        # If android version is S or later, uses bit mask to set and return.
+        version = self.ad.adb.shell(GET_BUILD_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]
+                self.ad.adb.shell(set_network_cmd)
+                get_network_cmd = 'cmd phone get-allowed-network-types-for-users'
+                allowed_network = self.ad.adb.shell(get_network_cmd)
+                self.log.info('The allowed network: {}'.format(allowed_network))
+                return
+        except ValueError:
+            self.log.info('The android version is older than S, use sl4a')
+
         if type == BaseCellularDut.PreferredNetworkType.LTE_ONLY:
             formatted_type = tel_utils.NETWORK_MODE_LTE_ONLY
         elif type == BaseCellularDut.PreferredNetworkType.WCDMA_ONLY:
diff --git a/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py b/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py
index 31c19fa..5bdbc1c 100644
--- a/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py
+++ b/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py
@@ -23,6 +23,7 @@
     LTE_ONLY = 'lte-only'
     GSM_ONLY = 'gsm-only'
     WCDMA_ONLY = 'wcdma-only'
+    NR_LTE = 'nr-lte'
 
 
 class BaseCellularDut():
diff --git a/acts/framework/acts/controllers/cellular_lib/BaseSimulation.py b/acts/framework/acts/controllers/cellular_lib/BaseSimulation.py
index 7427628..5e53786 100644
--- a/acts/framework/acts/controllers/cellular_lib/BaseSimulation.py
+++ b/acts/framework/acts/controllers/cellular_lib/BaseSimulation.py
@@ -68,7 +68,9 @@
     # the simulations inheriting from this class.
     DOWNLINK_SIGNAL_LEVEL_UNITS = None
 
-    def __init__(self, simulator, log, dut, test_config, calibration_table):
+    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
@@ -87,6 +89,7 @@
         self.log = log
         self.dut = dut
         self.calibration_table = calibration_table
+        self.nr_mode = nr_mode
 
         # Turn calibration on or off depending on the test config value. If the
         # key is not present, set to False by default
diff --git a/acts/framework/acts/controllers/cellular_lib/LteSimulation.py b/acts/framework/acts/controllers/cellular_lib/LteSimulation.py
index 245f4fa..346046c 100644
--- a/acts/framework/acts/controllers/cellular_lib/LteSimulation.py
+++ b/acts/framework/acts/controllers/cellular_lib/LteSimulation.py
@@ -411,7 +411,9 @@
         tdd_config4_tput_lut  # DL 256QAM, UL 64 QAM OFF & MAC padding ON
     }
 
-    def __init__(self, simulator, log, dut, test_config, calibration_table):
+    def __init__(
+        self, simulator, log, dut, test_config, calibration_table,
+        nr_mode=None):
         """ Initializes the simulator for a single-carrier LTE simulation.
 
         Args:
@@ -424,14 +426,19 @@
 
         """
 
-        super().__init__(simulator, log, dut, test_config, calibration_table)
+        super().__init__(
+            simulator, log, dut, test_config, calibration_table, nr_mode)
 
         self.num_carriers = None
 
         # Force device to LTE only so that it connects faster
         try:
-            self.dut.set_preferred_network_type(
-                BaseCellularDut.PreferredNetworkType.LTE_ONLY)
+            if self.nr_mode and 'nr' == self.nr_mode:
+                self.dut.set_preferred_network_type(
+                    BaseCellularDut.PreferredNetworkType.LTE_NR)
+            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.
@@ -447,7 +454,12 @@
 
     def setup_simulator(self):
         """ Do initial configuration in the simulator. """
-        self.simulator.setup_lte_scenario()
+        if self.nr_mode and 'nr' == self.nr_mode:
+            self.log.info('Initializes the callbox to Nr Nsa scenario')
+            self.simulator.setup_nr_nsa_scenario()
+        else:
+            self.log.info('Initializes the callbox to LTE scenario')
+            self.simulator.setup_lte_scenario()
 
     def configure(self, parameters):
         """ Configures simulation using a dictionary of parameters.
@@ -481,6 +493,7 @@
                     # If the remaining string is only the band number, add
                     # the cell and continue
                     new_cell_list.append(cell)
+                    continue
 
                 ca_class = band[-1].upper()
                 band_num = band[:-1]
@@ -591,7 +604,9 @@
 
         bandwidth = bts_config.bandwidth
 
-        if bandwidth == 20:  # 100 RBs
+        if bandwidth == 100: # This assumes 273 RBs. TODO: b/229163022
+            power = rsrp + 35.15
+        elif bandwidth == 20:  # 100 RBs
             power = rsrp + 30.79
         elif bandwidth == 15:  # 75 RBs
             power = rsrp + 29.54
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 e5b8fce..53c5839 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
@@ -85,10 +85,12 @@
             self.log.debug(f"Paused session: {response.get('result')}")
 
         # Acquire control of policy layer
+        controller_errors = []
         while time.time() < end_time:
             # Create a client controller
             response = self.device.wlan_policy_lib.wlanCreateClientController()
             if response.get('error'):
+                controller_errors.append(response['error'])
                 self.log.debug(response['error'])
                 time.sleep(1)
                 continue
@@ -96,11 +98,15 @@
             # channel, meaning the client controller was rejected.
             response = self.device.wlan_policy_lib.wlanGetSavedNetworks()
             if response.get('error'):
+                controller_errors.append(response['error'])
                 self.log.debug(response['error'])
                 time.sleep(1)
                 continue
             break
         else:
+            self.log.warning(
+                "Failed to create and use a WLAN policy client controller. Errors: ["
+                + "; ".join(controller_errors) + "]")
             raise WlanPolicyControllerError(
                 'Failed to create and use a WLAN policy client controller.')
 
diff --git a/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py b/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py
index e9dbde9..22b9d50 100644
--- a/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py
+++ b/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py
@@ -195,7 +195,8 @@
         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
 
@@ -225,54 +226,66 @@
         raise ValueError('The fuchsia_device ip must be the mDNS name to be '
                          'able to flash.')
 
+    file_to_download = None
+    image_archive_path = None
+    image_path = None
+
     if not fuchsia_device.specific_image:
-        file_download_needed = True
         product_build = fuchsia_device.product_type
         if fuchsia_device.build_type:
-            product_build = '{}_{}'.format(product_build,
-                                           fuchsia_device.build_type)
+            product_build = f'{product_build}_{fuchsia_device.build_type}'
         if 'LATEST' in fuchsia_device.build_number:
             sdk_version = 'sdk'
             if 'LATEST_F' in fuchsia_device.build_number:
                 f_branch = fuchsia_device.build_number.split('LATEST_F', 1)[1]
-                sdk_version = 'f{}_sdk'.format(f_branch)
-            file_to_download = '{}/{}-{}.{}-release.tgz'.format(
-                FUCHSIA_RELEASE_TESTING_URL, sdk_version, product_build,
-                fuchsia_device.board_type)
+                sdk_version = f'f{f_branch}_sdk'
+            file_to_download = (
+                f'{FUCHSIA_RELEASE_TESTING_URL}/'
+                f'{sdk_version}-{product_build}.{fuchsia_device.board_type}-release.tgz'
+            )
         else:
             # Must be a fully qualified build number (e.g. 5.20210721.4.1215)
-            file_to_download = '{}/{}/images/{}.{}-release.tgz'.format(
-                FUCHSIA_SDK_URL, fuchsia_device.build_number, product_build,
-                fuchsia_device.board_type)
+            file_to_download = (
+                f'{FUCHSIA_SDK_URL}/{fuchsia_device.build_number}/images/'
+                f'{product_build}.{fuchsia_device.board_type}-release.tgz')
     elif 'gs://' in fuchsia_device.specific_image:
-        file_download_needed = True
         file_to_download = fuchsia_device.specific_image
+    elif os.path.isdir(fuchsia_device.specific_image):
+        image_path = fuchsia_device.specific_image
     elif tarfile.is_tarfile(fuchsia_device.specific_image):
-        file_download_needed = False
-        file_to_download = fuchsia_device.specific_image
+        image_archive_path = fuchsia_device.specific_image
     else:
-        raise ValueError('A suitable build could not be found.')
+        raise ValueError(
+            f'Invalid specific_image "{fuchsia_device.specific_image}"')
 
-    tmp_dir = tempfile.TemporaryDirectory(suffix=fuchsia_device.board_type)
-    with tmp_dir as tmp_path:
-        if file_download_needed:
-            logging.info('Downloading %s to %s' % (file_to_download, tmp_path))
-            job.run('gsutil cp %s %s' % (file_to_download, tmp_dir.name))
-            image_tgz = os.path.join(tmp_path,
-                                     os.path.basename(file_to_download))
-        else:
-            image_tgz = file_to_download
-
-        # Use tar command instead of tarfile.extractall, as it takes too long.
-        job.run(f'tar xfvz {image_tgz} -C {tmp_path}', timeout=120)
-
+    if image_path:
         reboot_to_bootloader(fuchsia_device, use_ssh,
                              fuchsia_reconnect_after_reboot_time)
-
         logging.info(
-            f'Flashing {fuchsia_device.orig_ip} with {image_tgz} using authorized keys "{fuchsia_device.authorized_file}".'
+            f'Flashing {fuchsia_device.orig_ip} with {image_path} using authorized keys "{fuchsia_device.authorized_file}".'
         )
-        run_flash_script(fuchsia_device, tmp_path)
+        run_flash_script(fuchsia_device, image_path)
+    else:
+        suffix = fuchsia_device.board_type
+        with tempfile.TemporaryDirectory(suffix=suffix) as image_path:
+            if file_to_download:
+                logging.info(f'Downloading {file_to_download} to {image_path}')
+                job.run(f'gsutil cp {file_to_download} {image_path}')
+                image_archive_path = os.path.join(
+                    image_path, os.path.basename(file_to_download))
+
+            if image_archive_path:
+                # Use tar command instead of tarfile.extractall, as it takes too long.
+                job.run(f'tar xfvz {image_archive_path} -C {image_path}',
+                        timeout=120)
+
+            reboot_to_bootloader(fuchsia_device, use_ssh,
+                                 fuchsia_reconnect_after_reboot_time)
+
+            logging.info(
+                f'Flashing {fuchsia_device.orig_ip} with {image_archive_path} using authorized keys "{fuchsia_device.authorized_file}".'
+            )
+            run_flash_script(fuchsia_device, image_path)
     return True
 
 
diff --git a/acts/framework/setup.py b/acts/framework/setup.py
index 98a8fb0..21168fd 100755
--- a/acts/framework/setup.py
+++ b/acts/framework/setup.py
@@ -24,50 +24,50 @@
 
 install_requires = [
     'backoff',
+    'dlipower',
     # Future needs to have a newer version that contains urllib.
     'future>=0.16.0',
+    'grpcio',
+    'mobly>=1.10.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',
-    'pyserial',
-    'pyyaml>=5.1',
-    'pynacl==1.4.0',
-    'protobuf>=3.14.0',
-    'retry',
-    'requests',
-    'scapy',
-    'pylibftdi',
-    'xlsxwriter',
-    'mobly>=1.10.0',
-    'grpcio',
     'Monsoon',
     # paramiko-ng is needed vs paramiko as currently paramiko does not support
     # ed25519 ssh keys, which is what Fuchsia uses.
     'paramiko-ng',
-    'dlipower',
+    'protobuf>=3.14.0',
+    'pylibftdi',
+    'pynacl==1.4.0',
+    'pyserial',
+    'pyyaml>=5.1',
+    'requests',
+    'retry',
+    'scapy',
     'usbinfo',
+    'xlsxwriter',
     'zeroconf'
 ]
 
+versioned_deps = {
+    'numpy': 'numpy',
+    'scipy': 'scipy'
+}
+
 # numpy and scipy version matrix per:
 # https://docs.scipy.org/doc/scipy/reference/toolchain.html
+if sys.version_info < (3, 8):
+    versioned_deps['numpy'] = 'numpy<1.22'
+    versioned_deps['scipy'] = 'scipy<1.8'
+if sys.version_info < (3, 7):
+    versioned_deps['numpy'] = 'numpy<1.20'
+    versioned_deps['scipy'] = 'scipy<1.6'
+    versioned_deps['typing_extensions'] = 'typing_extensions==4.1.1'
 if sys.version_info < (3, 6):
-    # Python <= 3.5 uses scipy up to 1.4 and numpy up to 1.18.x
-    # b/157117302:Monsoon dependency
-    install_requires.append('scipy<1.5')
-    install_requires.append('numpy<1.19')
-elif sys.version_info < (3, 7):
-    # Python 3.6 uses scipy up to 1.5 and numpy up to 1.19.x
-    install_requires.append('scipy<1.6')
-    install_requires.append('numpy<1.20')
-elif sys.version_info < (3, 8):
-    # Python 3.7 uses latest scipy up to 1.7.x and numpy up to 1.21.x
-    install_requires.append('scipy<1.8')
-    install_requires.append('numpy<1.22')
-else:
-    # Python 3.8+ is supported by latest scipy and numpy
-    install_requires.append('scipy')
-    install_requires.append('numpy')
+    versioned_deps['numpy'] = 'numpy<1.19'
+    versioned_deps['scipy'] = 'scipy<1.5'
+
+install_requires += list(versioned_deps.values())
 
 if sys.version_info < (3, ):
     install_requires.append('enum34')
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 3f092e4..8e78a66 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
@@ -82,6 +82,8 @@
 
         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))
 
         # Unpack test parameters used in this class
         self.unpack_userparams(['custom_files'],
@@ -374,10 +376,21 @@
         cellular_dut = AndroidCellularDut.AndroidCellularDut(
             self.dut, self.log)
         # Instantiate a new simulation
-        self.simulation = simulation_class(self.cellular_simulator, self.log,
-                                           cellular_dut,
-                                           self.cellular_test_params,
-                                           self.calibration_table[sim_type])
+        if sim_type == self.PARAM_SIM_TYPE_NR:
+            self.simulation = simulation_class(
+                self.cellular_simulator,
+                self.log,
+                cellular_dut,
+                self.cellular_test_params,
+                self.calibration_table[sim_type],
+                nr_mode=self.PARAM_SIM_TYPE_NR)
+        else:
+            self.simulation = simulation_class(
+                self.cellular_simulator,
+                self.log,
+                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.