Update Wifi performances tests to enable 11ax tests

This CL enables testing both 11ac and 11ax tests using generically named
test cases so that test suites do not need to be replicated for each
different wifi standard. WiFi 6 phones will be tested in WiFi 6 setups
and WiFi 5 in WiFi 5. It is still possible to specify technology, e.g.,
test a WiFi 6 DUT in WiFi 5 mode.

Test: Done
Bug: None

Signed-off-by: Omar El Ayach <oelayach@google.com>
Change-Id: I2bb387ac41f5275d362c9d6f11e58e5160932dcb
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 988e673..a20936f 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/ota_sniffer.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/ota_sniffer.py
@@ -572,5 +572,5 @@
             network: dictionary of network credentials; SSID and password.
         """
 
-        self.log.debug('Connecting to network {}'.format(network['SSID']))
+        self.log.debug('Setting monitor mode on Ch {}, bw {}'.format(chan, bw))
         self.set_monitor_mode(chan, bw)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils.py
index b804bce..2fbb41d 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_performance_test_utils.py
@@ -654,7 +654,7 @@
     if ipv6:
         iperf_args = iperf_args + '-6 '
     if traffic_type.upper() == 'UDP':
-        iperf_args = iperf_args + '-u -b {} -l 1400 -P {} '.format(
+        iperf_args = iperf_args + '-u -b {} -l 1470 -P {} '.format(
             udp_throughput, num_processes)
     elif traffic_type.upper() == 'TCP':
         iperf_args = iperf_args + '-P {} '.format(num_processes)
@@ -715,7 +715,7 @@
     current_rssi = current_rssi['signal_poll_rssi']['mean']
     ping_future.result()
     target_atten = 0
-    logging.debug("RSSI @ {0:.2f}dB attenuation = {1:.2f}".format(
+    logging.debug('RSSI @ {0:.2f}dB attenuation = {1:.2f}'.format(
         target_atten, current_rssi))
     within_range = 0
     for idx in range(20):
@@ -740,7 +740,7 @@
                                           ignore_samples=1)
         current_rssi = current_rssi['signal_poll_rssi']['mean']
         ping_future.result()
-        logging.info("RSSI @ {0:.2f}dB attenuation = {1:.2f}".format(
+        logging.info('RSSI @ {0:.2f}dB attenuation = {1:.2f}'.format(
             target_atten, current_rssi))
         if abs(current_rssi - target_rssi) < 1:
             if within_range:
@@ -845,11 +845,11 @@
         for idx, chain in enumerate(rf_map_by_network[net_id]):
             if chain:
                 rf_map_by_atten[idx].append({
-                    "network": net_id,
-                    "dut_chain": chain
+                    'network': net_id,
+                    'dut_chain': chain
                 })
-    logging.debug("RF Map (by Network): {}".format(rf_map_by_network))
-    logging.debug("RF Map (by Atten): {}".format(rf_map_by_atten))
+    logging.debug('RF Map (by Network): {}'.format(rf_map_by_network))
+    logging.debug('RF Map (by Atten): {}'.format(rf_map_by_atten))
 
     return rf_map_by_network, rf_map_by_atten
 
@@ -899,7 +899,7 @@
             break
         time.sleep(SHORT_SLEEP)
     elapsed_time = time.time() - start_time
-    logging.debug("DUT Final Temperature: {}C. Cooldown duration: {}".format(
+    logging.debug('DUT Final Temperature: {}C. Cooldown duration: {}'.format(
         temperature, elapsed_time))
 
 
@@ -920,22 +920,22 @@
     health_check = True
     battery_level = utils.get_battery_level(dut)
     if battery_level < batt_thresh:
-        logging.warning("Battery level low ({}%)".format(battery_level))
+        logging.warning('Battery level low ({}%)'.format(battery_level))
         health_check = False
     else:
-        logging.debug("Battery level = {}%".format(battery_level))
+        logging.debug('Battery level = {}%'.format(battery_level))
 
     temperature = get_dut_temperature(dut)
     if temperature > temp_threshold:
         if cooldown:
             logging.warning(
-                "Waiting for DUT to cooldown. ({} C)".format(temperature))
+                'Waiting for DUT to cooldown. ({} C)'.format(temperature))
             wait_for_dut_cooldown(dut, target_temp=temp_threshold - 5)
         else:
-            logging.warning("DUT Overheating ({} C)".format(temperature))
+            logging.warning('DUT Overheating ({} C)'.format(temperature))
             health_check = False
     else:
-        logging.debug("DUT Temperature = {} C".format(temperature))
+        logging.debug('DUT Temperature = {} C'.format(temperature))
     return health_check
 
 
@@ -1418,10 +1418,10 @@
             for field_name, field_value in ini_field_dict.items():
                 line_regex = re.compile(template_regex.format(field_name))
                 if re.match(line_regex, line):
-                    ini_lines[idx] = "{}={}".format(field_name, field_value)
+                    ini_lines[idx] = '{}={}'.format(field_name, field_value)
                     print(ini_lines[idx])
     with open(ini_file_path, 'w') as f:
-        f.write("\n".join(ini_lines) + "\n")
+        f.write('\n'.join(ini_lines) + '\n')
 
 
 def _edit_dut_ini(dut, ini_fields):
@@ -1460,16 +1460,16 @@
 
 def set_ini_tx_mode(dut, mode):
     TX_MODE_DICT = {
-        "Auto": 0,
-        "11n": 4,
-        "11ac": 9,
-        "11abg": 1,
-        "11b": 2,
-        "11g": 3,
-        "11g only": 5,
-        "11n only": 6,
-        "11b only": 7,
-        "11ac only": 8
+        'Auto': 0,
+        '11n': 4,
+        '11ac': 9,
+        '11abg': 1,
+        '11b': 2,
+        '11': 3,
+        '11g only': 5,
+        '11n only': 6,
+        '11b only': 7,
+        '11ac only': 8
     }
 
     ini_fields = {
@@ -1647,11 +1647,11 @@
             try:
                 rate_info = self.dut.adb.shell('wl rate_info', timeout=0.1)
                 self.llstats_incremental['summary'][
-                    'common_tx_mcs'] = "{} Mbps".format(
+                    'common_tx_mcs'] = '{} Mbps'.format(
                         re.findall('\[Tx\]:'
                                    ' (\d+[.]*\d* Mbps)', rate_info))
                 self.llstats_incremental['summary'][
-                    'common_rx_mcs'] = "{} Mbps".format(
+                    'common_rx_mcs'] = '{} Mbps'.format(
                         re.findall('\[Rx\]:'
                                    ' (\d+[.]*\d* Mbps)', rate_info))
             except:
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 f45bb49..a3bd0df 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
@@ -2,18 +2,20 @@
 #
 #   Copyright 2020 - The Android Open Source Project
 #
-#   Licensed under the Apache License, Version 2.0 (the "License");
+#   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,
+#   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.abc
+import copy
 import fcntl
 import importlib
 import os
@@ -36,48 +38,48 @@
         the following: brand, model, ip_address, username and password
     """
     SUPPORTED_APS = {
-        ("Netgear", "R7000"): {
+        ('Netgear', 'R7000'): {
             'name': 'NetgearR7000AP',
             'package': 'netgear_r7000'
         },
-        ("Netgear", "R7000NA"): {
+        ('Netgear', 'R7000NA'): {
             'name': 'NetgearR7000NAAP',
             'package': 'netgear_r7000'
         },
-        ("Netgear", "R7500"): {
+        ('Netgear', 'R7500'): {
             'name': 'NetgearR7500AP',
             'package': 'netgear_r7500'
         },
-        ("Netgear", "R7800"): {
+        ('Netgear', 'R7800'): {
             'name': 'NetgearR7800AP',
             'package': 'netgear_r7800'
         },
-        ("Netgear", "R8000"): {
+        ('Netgear', 'R8000'): {
             'name': 'NetgearR8000AP',
             'package': 'netgear_r8000'
         },
-        ("Netgear", "R8500"): {
-            'name': 'NetgearR8500AP',
-            'package': 'netgear_r8500'
-        },
-        ("Netgear", "RAX80"): {
+        ('Netgear', 'RAX80'): {
             'name': 'NetgearRAX80AP',
             'package': 'netgear_rax80'
         },
-        ("Netgear", "RAX120"): {
+        ('Netgear', 'RAX200'): {
+            'name': 'NetgearRAX200AP',
+            'package': 'netgear_rax200'
+        },
+        ('Netgear', 'RAX120'): {
             'name': 'NetgearRAX120AP',
             'package': 'netgear_rax120'
         },
-        ("Google", "Wifi"): {
+        ('Google', 'Wifi'): {
             'name': 'GoogleWifiAP',
             'package': 'google_wifi'
         },
     }
     objs = []
     for config in configs:
-        ap_id = (config["brand"], config["model"])
+        ap_id = (config['brand'], config['model'])
         if ap_id not in SUPPORTED_APS:
-            raise KeyError("Invalid retail AP brand and model combination.")
+            raise KeyError('Invalid retail AP brand and model combination.')
         ap_class_dict = SUPPORTED_APS[ap_id]
         ap_package = 'acts_contrib.test_utils.wifi.wifi_retail_ap.{}'.format(
             ap_class_dict['package'])
@@ -108,20 +110,20 @@
             headless: boolean to control visible/headless browser operation
             timeout: maximum time allowed to launch browser
         """
-        self.log = logger.create_tagged_trace_logger("ChromeDriver")
+        self.log = logger.create_tagged_trace_logger('ChromeDriver')
         self.chrome_options = splinter.driver.webdriver.chrome.Options()
-        self.chrome_options.add_argument("--no-proxy-server")
-        self.chrome_options.add_argument("--no-sandbox")
-        self.chrome_options.add_argument("--allow-running-insecure-content")
-        self.chrome_options.add_argument("--ignore-certificate-errors")
+        self.chrome_options.add_argument('--no-proxy-server')
+        self.chrome_options.add_argument('--no-sandbox')
+        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(
         )
-        self.chrome_capabilities["acceptSslCerts"] = True
-        self.chrome_capabilities["acceptInsecureCerts"] = True
+        self.chrome_capabilities['acceptSslCerts'] = True
+        self.chrome_capabilities['acceptInsecureCerts'] = True
         if headless:
-            self.chrome_options.add_argument("--headless")
-            self.chrome_options.add_argument("--disable-gpu")
-        self.lock_file_path = "/usr/local/bin/chromedriver"
+            self.chrome_options.add_argument('--headless')
+            self.chrome_options.add_argument('--disable-gpu')
+        self.lock_file_path = '/usr/local/bin/chromedriver'
         self.timeout = timeout
 
     def __enter__(self):
@@ -132,7 +134,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.lock_file_path, 'r')
         start_time = time.time()
         while time.time() < start_time + self.timeout:
             try:
@@ -153,9 +155,9 @@
             except:
                 fcntl.flock(self.lock_file, fcntl.LOCK_UN)
                 self.lock_file.close()
-                raise RuntimeError("Error starting browser. "
-                                   "Releasing lock file.")
-        raise TimeoutError("Could not start chrome browser in time.")
+                raise RuntimeError('Error starting browser. '
+                                   'Releasing lock file.')
+        raise TimeoutError('Could not start chrome browser in time.')
 
     def __exit__(self, exc_type, exc_value, traceback):
         """Exit context manager for BlockingBrowser.
@@ -167,7 +169,7 @@
             super(BlockingBrowser, self).__exit__(exc_type, exc_value,
                                                   traceback)
         except:
-            raise RuntimeError("Failed to quit browser. Releasing lock file.")
+            raise RuntimeError('Failed to quit browser. Releasing lock file.')
         finally:
             fcntl.flock(self.lock_file, fcntl.LOCK_UN)
             self.lock_file.close()
@@ -181,7 +183,7 @@
                          url,
                          page_load_timeout,
                          num_tries,
-                         backup_url="about:blank",
+                         backup_url='about:blank',
                          check_for_element=None):
         """Method to visit webpages and retry upon failure.
 
@@ -204,7 +206,7 @@
             except:
                 self.restart()
 
-            page_reached = self.url.split("/")[-1] == url.split("/")[-1]
+            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)
@@ -219,9 +221,9 @@
                     self.restart()
 
             if idx == num_tries - 1:
-                self.log.error("URL unreachable. Current URL: {}".format(
+                self.log.error('URL unreachable. Current URL: {}'.format(
                     self.url))
-                raise RuntimeError("URL unreachable.")
+                raise RuntimeError('URL unreachable.')
 
 
 class WifiRetailAP(object):
@@ -233,8 +235,17 @@
     """
     def __init__(self, ap_settings):
         self.ap_settings = ap_settings.copy()
-        self.log = logger.create_tagged_trace_logger("AccessPoint|{}".format(
+        self.log = logger.create_tagged_trace_logger('AccessPoint|{}'.format(
             self._get_control_ip_address()))
+        # Capabilities variable describing AP capabilities
+        self.capabilities = {
+            'interfaces': [],
+            'channels': {},
+            'modes': {},
+            'default_mode': None
+        }
+        for interface in self.capabilities['interfaces']:
+            self.ap_settings.setdefault(interface, {})
         # Lock AP
         if self.ap_settings.get('lock_ap', 0):
             self.lock_timeout = self.ap_settings.get('lock_timeout', 3600)
@@ -272,11 +283,12 @@
         Raises:
             ValueError: If read AP settings do not match stored settings.
         """
-        assumed_ap_settings = self.ap_settings.copy()
+        assumed_ap_settings = copy.deepcopy(self.ap_settings)
         actual_ap_settings = self.read_ap_settings()
+
         if assumed_ap_settings != actual_ap_settings:
             self.log.warning(
-                "Discrepancy in AP settings. Some settings may have been overwritten."
+                'Discrepancy in AP settings. Some settings may have been overwritten.'
             )
 
     def configure_ap(self, **config_flags):
@@ -300,8 +312,8 @@
         Args:
             region: string indicating AP region
         """
-        self.log.warning("Updating region may overwrite wireless settings.")
-        setting_to_update = {"region": region}
+        self.log.warning('Updating region may overwrite wireless settings.')
+        setting_to_update = {'region': region}
         self.update_ap_settings(setting_to_update)
 
     def set_radio_on_off(self, network, status):
@@ -311,7 +323,7 @@
             network: string containing network identifier (2G, 5G_1, 5G_2)
             status: boolean indicating on or off (0: off, 1: on)
         """
-        setting_to_update = {"status_{}".format(network): int(status)}
+        setting_to_update = {network: {'status': int(status)}}
         self.update_ap_settings(setting_to_update)
 
     def set_ssid(self, network, ssid):
@@ -321,7 +333,7 @@
             network: string containing network identifier (2G, 5G_1, 5G_2)
             ssid: string containing ssid
         """
-        setting_to_update = {"ssid_{}".format(network): str(ssid)}
+        setting_to_update = {network: {'ssid': str(ssid)}}
         self.update_ap_settings(setting_to_update)
 
     def set_channel(self, network, channel):
@@ -331,7 +343,10 @@
             network: string containing network identifier (2G, 5G_1, 5G_2)
             channel: string or int containing channel
         """
-        setting_to_update = {"channel_{}".format(network): str(channel)}
+        if channel not in self.capabilities['channels'][network]:
+            self.log.error('Ch{} is not supported on {} interface.'.format(
+                channel, network))
+        setting_to_update = {network: {'channel': str(channel)}}
         self.update_ap_settings(setting_to_update)
 
     def set_bandwidth(self, network, bandwidth):
@@ -341,9 +356,13 @@
             network: string containing network identifier (2G, 5G_1, 5G_2)
             bandwidth: string containing mode, e.g. 11g, VHT20, VHT40, VHT80.
         """
-        if 'BW' in bandwidth:
-            bandwidth = bandwidth.replace('BW', self.default_mode)
-        setting_to_update = {"bandwidth_{}".format(network): str(bandwidth)}
+        if 'bw' in bandwidth:
+            bandwidth = bandwidth.replace('bw',
+                                          self.capabilities['default_mode'])
+        if bandwidth not in self.capabilities['modes'][network]:
+            self.log.error('{} mode is not supported on {} interface.'.format(
+                bandwidth, network))
+        setting_to_update = {network: {'bandwidth': str(bandwidth)}}
         self.update_ap_settings(setting_to_update)
 
     def set_power(self, network, power):
@@ -353,7 +372,10 @@
             network: string containing network identifier (2G, 5G_1, 5G_2)
             power: string containing power level, e.g., 25%, 100%
         """
-        setting_to_update = {"power_{}".format(network): str(power)}
+        if 'power' not in self.ap_settings[network].keys():
+            self.log.error(
+                'Cannot configure power on {} interface.'.format(network))
+        setting_to_update = {network: {'power': str(power)}}
         self.update_ap_settings(setting_to_update)
 
     def set_security(self, network, security_type, *password):
@@ -366,12 +388,16 @@
         """
         if (len(password) == 1) and (type(password[0]) == str):
             setting_to_update = {
-                "security_type_{}".format(network): str(security_type),
-                "password_{}".format(network): str(password[0])
+                network: {
+                    'security_type': str(security_type),
+                    'password': str(password[0])
+                }
             }
         else:
             setting_to_update = {
-                "security_type_{}".format(network): str(security_type)
+                network: {
+                    'security_type': str(security_type)
+                }
             }
         self.update_ap_settings(setting_to_update)
 
@@ -383,6 +409,27 @@
         """
         raise NotImplementedError
 
+    def _update_settings_dict(self,
+                              settings,
+                              updates,
+                              updates_requested=False,
+                              status_toggle_flag=False):
+        new_settings = copy.deepcopy(settings)
+        for key, value in updates.items():
+            if key not in new_settings.keys():
+                raise KeyError('{} is an invalid settings key.'.format(key))
+            elif isinstance(value, collections.abc.Mapping):
+                new_settings[
+                    key], updates_requested, status_toggle_flag = self._update_settings_dict(
+                        new_settings.get(key, {}), value, updates_requested,
+                        status_toggle_flag)
+            elif new_settings[key] != value:
+                new_settings[key] = value
+                updates_requested = True
+                if 'status' in key:
+                    status_toggle_flag = True
+        return new_settings, updates_requested, status_toggle_flag
+
     def update_ap_settings(self, dict_settings={}, **named_settings):
         """Function to update settings of existing AP.
 
@@ -396,24 +443,12 @@
         """
         settings_to_update = dict(dict_settings, **named_settings)
         if len(settings_to_update) != len(dict_settings) + len(named_settings):
-            raise KeyError("The following keys were passed twice: {}".format(
+            raise KeyError('The following keys were passed twice: {}'.format(
                 (set(dict_settings.keys()).intersection(
                     set(named_settings.keys())))))
-        if not set(settings_to_update.keys()).issubset(
-                set(self.ap_settings.keys())):
-            raise KeyError(
-                "The following settings are invalid for this AP: {}".format(
-                    set(settings_to_update.keys()).difference(
-                        set(self.ap_settings.keys()))))
 
-        updates_requested = False
-        status_toggle_flag = False
-        for setting, value in settings_to_update.items():
-            if self.ap_settings[setting] != value:
-                self.ap_settings[setting] = value
-                if "status" in setting:
-                    status_toggle_flag = True
-                updates_requested = True
+        self.ap_settings, updates_requested, status_toggle_flag = self._update_settings_dict(
+            self.ap_settings, settings_to_update)
 
         if updates_requested:
             self.configure_ap(status_toggled=status_toggle_flag)
@@ -426,27 +461,27 @@
         Returns:
             band: name of band which this channel belongs to on this ap
         """
-        for key, value in self.channel_band_map.items():
+        for key, value in self.capabilities['channels'].items():
             if channel in value:
                 return key
-        raise ValueError("Invalid channel passed in argument.")
+        raise ValueError('Invalid channel passed in argument.')
 
     def _get_control_ip_address(self):
         """Function to get AP's Control Interface IP address."""
-        if "ssh_config" in self.ap_settings.keys():
-            return self.ap_settings["ssh_config"]["host"]
+        if 'ssh_config' in self.ap_settings.keys():
+            return self.ap_settings['ssh_config']['host']
         else:
-            return self.ap_settings["ip_address"]
+            return self.ap_settings['ip_address']
 
     def _lock_ap(self):
         """Function to lock the ap while tests are running."""
-        self.lock_file_path = "/tmp/{}_{}_{}.lock".format(
+        self.lock_file_path = '/tmp/{}_{}_{}.lock'.format(
             self.ap_settings['brand'], self.ap_settings['model'],
             self._get_control_ip_address())
         if not os.path.exists(self.lock_file_path):
             with open(self.lock_file_path, 'w'):
                 pass
-        self.lock_file = open(self.lock_file_path, "r")
+        self.lock_file = open(self.lock_file_path, 'r')
         start_time = time.time()
         self.log.info('Trying to acquire AP lock.')
         while time.time() < start_time + self.lock_timeout:
@@ -457,11 +492,11 @@
                 continue
             self.log.info('AP lock acquired.')
             return
-        raise RuntimeError("Could not lock AP in time.")
+        raise RuntimeError('Could not lock AP in time.')
 
     def _unlock_ap(self):
         """Function to unlock the AP when tests are done."""
         self.log.info('Releasing AP lock.')
-        if hasattr(self, "lock_file"):
+        if hasattr(self, 'lock_file'):
             fcntl.flock(self.lock_file, fcntl.LOCK_UN)
             self.lock_file.close()
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 7eee93e..dac7673 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
@@ -2,18 +2,19 @@
 #
 #   Copyright 2020 - The Android Open Source Project
 #
-#   Licensed under the Apache License, Version 2.0 (the "License");
+#   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,
+#   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.abc
 from acts.controllers import access_point
 from acts.controllers.ap_lib import bridge_interface
 from acts.controllers.ap_lib import hostapd_security
@@ -29,60 +30,82 @@
     def __init__(self, ap_settings):
         super().__init__(ap_settings)
         # Initialize AP
-        if self.ap_settings["status_2G"] and self.ap_settings["status_5G_1"]:
-            raise ValueError("Error initializing Google Wifi AP. "
-                             "Only one interface can be enabled at a time.")
-        self.channel_band_map = {
-            "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
-            "5G_1": [36, 40, 44, 48, 149, 153, 157, 161, 165]
+        if self.ap_settings['2G']['status'] and self.ap_settings['5G_1'][
+                'status']:
+            raise ValueError('Error initializing Google Wifi AP. '
+                             'Only one interface can be enabled at a time.')
+
+        self.capabilities = {
+            'interfaces': ['2G', '5G_1'],
+            'channels': {
+                '2G': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+                '5G_1': [
+                    36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116,
+                    120, 124, 128, 132, 136, 140, 149, 153, 157, 161, 165
+                ]
+            },
+            'modes': {
+                '2G': ['VHT20', 'VHT40'],
+                '5G_1': ['VHT20', 'VHT40', 'VHT80']
+            },
+            'default_mode': 'VHT'
         }
+        for interface in self.capabilities['interfaces']:
+            self.ap_settings.setdefault(interface, {})
+
         self.BW_MODE_MAP = {
-            "legacy": 20,
-            "VHT20": 20,
-            "VHT40": 40,
-            "VHT80": 80
+            'legacy': 20,
+            'VHT20': 20,
+            'VHT40': 40,
+            'VHT80': 80
         }
         self.default_settings = {
-            "region": "United States",
-            "brand": "Google",
-            "model": "Wifi",
-            "hostapd_profile": "whirlwind",
-            "status_2G": 0,
-            "status_5G_1": 0,
-            "ssid_2G": "GoogleWifi_2G",
-            "ssid_5G_1": "GoogleWifi_5G",
-            "channel_2G": 11,
-            "channel_5G_1": 149,
-            "bandwidth_2G": "VHT20",
-            "bandwidth_5G_1": "VHT20",
-            "power_2G": "auto",
-            "power_5G_1": "auto",
-            "mode_2G": None,
-            "num_streams_2G": None,
-            "rate_2G": "auto",
-            "short_gi_2G": 0,
-            "mode_5G_1": None,
-            "num_streams_5G_1": None,
-            "rate_5G_1": "auto",
-            "short_gi_5G_1": 0,
-            "security_type_2G": "Open",
-            "security_type_5G_1": "Open",
-            "subnet_2G": "192.168.1.0/24",
-            "subnet_5G_1": "192.168.9.0/24",
-            "password_2G": "password",
-            "password_5G_1": "password"
+            'region': 'United States',
+            'brand': 'Google',
+            'model': 'Wifi',
+            'hostapd_profile': 'whirlwind',
+            '2G': {
+                'status': 0,
+                'ssid': 'GoogleWifi_2G',
+                'channel': 11,
+                'bandwidth': 'VHT20',
+                'power': 'auto',
+                'mode': None,
+                'num_streams': None,
+                'rate': 'auto',
+                'short_gi': 0,
+                'security_type': 'Open',
+                'password': 'password',
+                'subnet': '192.168.1.0/24'
+            },
+            '5G_1': {
+                'status': 0,
+                'ssid': 'GoogleWifi_2G',
+                'channel': 11,
+                'bandwidth': 'VHT20',
+                'power': 'auto',
+                'mode': None,
+                'num_streams': None,
+                'rate': 'auto',
+                'short_gi': 0,
+                'security_type': 'Open',
+                'password': 'password',
+                'subnet': '192.168.9.0/24'
+            }
         }
-        self.default_mode = 'VHT'
-        for setting in self.default_settings.keys():
-            if setting not in self.ap_settings:
-                self.log.debug(
-                    "{0} not found during init. Setting {0} = {1}".format(
-                        setting, self.default_settings[setting]))
-                self.ap_settings[setting] = self.default_settings[setting]
+        for interface in self.capabilities['interfaces']:
+            for setting in self.default_settings[interface].keys():
+                if setting not in self.ap_settings[interface]:
+                    self.log.debug(
+                        '{0} {1} not found during init. Setting {1} = {2}'.
+                        format(interface, setting,
+                               self.default_settings[interface][setting]))
+                    self.ap_settings[interface][
+                        setting] = self.default_settings[interface][setting]
         init_settings = self.ap_settings.copy()
-        init_settings["ap_subnet"] = {
-            "2g": self.ap_settings["subnet_2G"],
-            "5g": self.ap_settings["subnet_5G_1"]
+        init_settings['ap_subnet'] = {
+            '2g': self.ap_settings['2G']['subnet'],
+            '5g': self.ap_settings['5G_1']['subnet']
         }
         self.access_point = access_point.AccessPoint(init_settings)
         self.configure_ap()
@@ -104,122 +127,109 @@
         """
         settings_to_update = dict(dict_settings, **named_settings)
         if len(settings_to_update) != len(dict_settings) + len(named_settings):
-            raise KeyError("The following keys were passed twice: {}".format(
+            raise KeyError('The following keys were passed twice: {}'.format(
                 (set(dict_settings.keys()).intersection(
                     set(named_settings.keys())))))
-        if not set(settings_to_update.keys()).issubset(
-                set(self.ap_settings.keys())):
-            raise KeyError(
-                "The following settings are invalid for this AP: {}".format(
-                    set(settings_to_update.keys()).difference(
-                        set(self.ap_settings.keys()))))
 
-        updating_2G = any(["2G" in x for x in settings_to_update.keys()])
-        updating_5G_1 = any(["5G_1" in x for x in settings_to_update.keys()])
+        updating_2G = '2G' in settings_to_update.keys()
+        updating_5G_1 = '5G_1' in settings_to_update.keys()
         if updating_2G and updating_5G_1:
             raise ValueError(
-                "Error updating Google WiFi AP. "
-                "One interface can be activated and updated at a time")
+                'Error updating Google WiFi AP. '
+                'One interface can be activated and updated at a time')
         elif updating_2G:
             # If updating an interface and not explicitly setting its status,
             # it is assumed that the interface is to be ENABLED and updated
-            if "status_2G" not in settings_to_update:
-                settings_to_update["status_2G"] = 1
-                settings_to_update["status_5G_1"] = 0
+            if 'status' not in settings_to_update['2G']:
+                settings_to_update['2G']['status'] = 1
+                settings_to_update['5G_1'] = {'status': 0}
         elif updating_5G_1:
-            if "status_5G_1" not in settings_to_update:
-                settings_to_update["status_2G"] = 0
-                settings_to_update["status_5G_1"] = 1
-
-        updates_requested = False
-        for setting, value in settings_to_update.items():
-            if self.ap_settings[setting] != value:
-                self.ap_settings[setting] = value
-                updates_requested = True
-
+            if 'status' not in settings_to_update['5G_1']:
+                settings_to_update['2G'] = {'status': 0}
+                settings_to_update['5G_1']['status'] = 1
+        self.ap_settings, updates_requested, status_toggle_flag = self._update_settings_dict(
+            self.ap_settings, settings_to_update)
         if updates_requested:
             self.configure_ap()
 
     def configure_ap(self):
         """Function to configure Google Wifi."""
-        self.log.info("Stopping Google Wifi interfaces.")
+        self.log.info('Stopping Google Wifi interfaces.')
+        print(self.ap_settings)
         self.access_point.stop_all_aps()
 
-        if self.ap_settings["status_2G"] == 1:
-            network = "2G"
-            self.log.info("Bringing up 2.4 GHz network.")
-        elif self.ap_settings["status_5G_1"] == 1:
-            network = "5G_1"
-            self.log.info("Bringing up 5 GHz network.")
+        if self.ap_settings['2G']['status'] == 1:
+            interface = '2G'
+            self.log.info('Bringing up 2.4 GHz interface.')
+        elif self.ap_settings['5G_1']['status'] == 1:
+            interface = '5G_1'
+            self.log.info('Bringing up 5 GHz interface.')
         else:
             return
 
         bss_settings = []
-        ssid = self.ap_settings["ssid_{}".format(network)]
-        security_mode = self.ap_settings["security_type_{}".format(
-            network)].lower()
-        if "wpa" in security_mode:
-            password = self.ap_settings["password_{}".format(network)]
+        ssid = self.ap_settings[interface]['ssid']
+        security_mode = self.ap_settings[interface]['security_type'].lower()
+        if 'wpa' in security_mode:
+            password = self.ap_settings[interface]['password']
             security = hostapd_security.Security(security_mode=security_mode,
                                                  password=password)
         else:
             security = hostapd_security.Security(security_mode=None,
                                                  password=None)
-        channel = int(self.ap_settings["channel_{}".format(network)])
-        bandwidth = self.BW_MODE_MAP[self.ap_settings["bandwidth_{}".format(
-            network)]]
+        channel = int(self.ap_settings[interface]['channel'])
+        bandwidth = self.BW_MODE_MAP[self.ap_settings[interface]['bandwidth']]
         config = hostapd_ap_preset.create_ap_preset(
             channel=channel,
             ssid=ssid,
             security=security,
             bss_settings=bss_settings,
             vht_bandwidth=bandwidth,
-            profile_name=self.ap_settings["hostapd_profile"],
+            profile_name=self.ap_settings['hostapd_profile'],
             iface_wlan_2g=self.access_point.wlan_2g,
             iface_wlan_5g=self.access_point.wlan_5g)
         config_bridge = self.access_point.generate_bridge_configs(channel)
         brconfigs = bridge_interface.BridgeInterfaceConfigs(
-            config_bridge[0], "lan0", config_bridge[2])
+            config_bridge[0], 'lan0', config_bridge[2])
         self.access_point.bridge.startup(brconfigs)
         self.access_point.start_ap(config)
-        self.set_power(network, self.ap_settings["power_{}".format(network)])
-        self.set_rate(
-            network,
-            mode=self.ap_settings["mode_{}".format(network)],
-            num_streams=self.ap_settings["num_streams_{}".format(network)],
-            rate=self.ap_settings["rate_{}".format(network)],
-            short_gi=self.ap_settings["short_gi_{}".format(network)])
-        self.log.info("AP started on channel {} with SSID {}".format(
+        self.set_power(interface, self.ap_settings[interface]['power'])
+        self.set_rate(interface,
+                      mode=self.ap_settings[interface]['mode'],
+                      num_streams=self.ap_settings[interface]['num_streams'],
+                      rate=self.ap_settings[interface]['rate'],
+                      short_gi=self.ap_settings[interface]['short_gi'])
+        self.log.info('AP started on channel {} with SSID {}'.format(
             channel, ssid))
 
-    def set_power(self, network, power):
-        """Function that sets network transmit power.
+    def set_power(self, interface, power):
+        """Function that sets interface transmit power.
 
         Args:
-            network: string containing network identifier (2G, 5G_1, 5G_2)
+            interface: string containing interface identifier (2G, 5G_1)
             power: power level in dBm
         """
-        if power == "auto":
-            power_string = "auto"
+        if power == 'auto':
+            power_string = 'auto'
         else:
             if not float(power).is_integer():
                 self.log.info(
-                    "Power in dBm must be an integer. Setting to {}".format(
+                    'Power in dBm must be an integer. Setting to {}'.format(
                         int(power)))
             power = int(power)
-            power_string = "fixed {}".format(int(power) * 100)
+            power_string = 'fixed {}'.format(int(power) * 100)
 
-        if "2G" in network:
-            interface = self.access_point.wlan_2g
-            self.ap_settings["power_2G"] = power
-        elif "5G_1" in network:
-            interface = self.access_point.wlan_5g
-            self.ap_settings["power_5G_1"] = power
-        self.access_point.ssh.run("iw dev {} set txpower {}".format(
-            interface, power_string))
+        if '2G' in interface:
+            interface_long = self.access_point.wlan_2g
+            self.ap_settings[interface]['power'] = power
+        elif '5G_1' in interface:
+            interface_long = self.access_point.wlan_5g
+            self.ap_settings[interface]['power'] = power
+        self.access_point.ssh.run('iw dev {} set txpower {}'.format(
+            interface_long, power_string))
 
     def set_rate(self,
-                 network,
+                 interface,
                  mode=None,
                  num_streams=None,
                  rate='auto',
@@ -227,40 +237,36 @@
         """Function that sets rate.
 
         Args:
-            network: string containing network identifier (2G, 5G_1, 5G_2)
+            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
         """
-        if "2G" in network:
-            interface = self.access_point.wlan_2g
-            interface_short = "2.4"
-            self.ap_settings["mode_2G"] = mode
-            self.ap_settings["num_streams_2G"] = num_streams
-            self.ap_settings["rate_2G"] = rate
-            self.ap_settings["short_gi_2G"] = short_gi
-        elif "5G_1" in network:
-            interface = self.access_point.wlan_5g
-            interface_short = "5"
-            self.ap_settings["mode_5G_1"] = mode
-            self.ap_settings["num_streams_5G_1"] = num_streams
-            self.ap_settings["rate_5G_1"] = rate
-            self.ap_settings["short_gi_5G_1"] = short_gi
+        if '2G' in interface:
+            interface_long = self.access_point.wlan_2g
+            interface_short = '2.4'
+        elif '5G_1' in interface:
+            interface_long = self.access_point.wlan_5g
+            interface_short = '5'
+        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
 
-        if rate == "auto":
-            cmd_string = "iw dev {0} set bitrates".format(interface)
-        elif "legacy" in mode.lower():
-            cmd_string = "iw dev {0} set bitrates legacy-{1} {2} ht-mcs-{1} vht-mcs-{1}".format(
-                interface, interface_short, rate)
-        elif "vht" in mode.lower():
-            cmd_string = "iw dev {0} set bitrates legacy-{1} ht-mcs-{1} vht-mcs-{1} {2}:{3}".format(
-                interface, interface_short, num_streams, rate)
+        if rate == 'auto':
+            cmd_string = 'iw dev {0} set bitrates'.format(interface_long)
+        elif 'legacy' in mode.lower():
+            cmd_string = 'iw dev {0} set bitrates legacy-{1} {2} ht-mcs-{1} vht-mcs-{1}'.format(
+                interface_long, interface_short, rate)
+        elif 'vht' in mode.lower():
+            cmd_string = 'iw dev {0} set bitrates legacy-{1} ht-mcs-{1} vht-mcs-{1} {2}:{3}'.format(
+                interface_long, interface_short, num_streams, rate)
             if short_gi:
-                cmd_string = cmd_string + " sgi-{}".format(interface_short)
-        elif "ht" in mode.lower():
-            cmd_string = "iw dev {0} set bitrates legacy-{1} ht-mcs-{1} {2} vht-mcs-{1}".format(
-                interface, interface_short, rate)
+                cmd_string = cmd_string + ' sgi-{}'.format(interface_short)
+        elif 'ht' in mode.lower():
+            cmd_string = 'iw dev {0} set bitrates legacy-{1} ht-mcs-{1} {2} vht-mcs-{1}'.format(
+                interface_long, interface_short, rate)
             if short_gi:
-                cmd_string = cmd_string + " sgi-{}".format(interface_short)
+                cmd_string = cmd_string + ' sgi-{}'.format(interface_short)
         self.access_point.ssh.run(cmd_string)
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 4f7a84e..e7f4e83 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
@@ -2,14 +2,14 @@
 #
 #   Copyright 2020 - The Android Open Source Project
 #
-#   Licensed under the Apache License, Version 2.0 (the "License");
+#   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,
+#   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.
@@ -31,149 +31,155 @@
         self.init_gui_data()
         # Read and update AP settings
         self.read_ap_settings()
-        if not set(ap_settings.items()).issubset(self.ap_settings.items()):
-            self.update_ap_settings(ap_settings)
+        self.update_ap_settings(ap_settings)
 
     def init_gui_data(self):
         """Function to initialize data used while interacting with web GUI"""
         self.config_page = (
-            "{protocol}://{username}:{password}@"
-            "{ip_address}:{port}/WLG_wireless_dual_band_r10.htm").format(
-                protocol=self.ap_settings["protocol"],
-                username=self.ap_settings["admin_username"],
-                password=self.ap_settings["admin_password"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
+            '{protocol}://{username}:{password}@'
+            '{ip_address}:{port}/WLG_wireless_dual_band_r10.htm').format(
+                protocol=self.ap_settings['protocol'],
+                username=self.ap_settings['admin_username'],
+                password=self.ap_settings['admin_password'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
         self.config_page_nologin = (
-            "{protocol}://{ip_address}:{port}/"
-            "WLG_wireless_dual_band_r10.htm").format(
-                protocol=self.ap_settings["protocol"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
+            '{protocol}://{ip_address}:{port}/'
+            'WLG_wireless_dual_band_r10.htm').format(
+                protocol=self.ap_settings['protocol'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
         self.config_page_advanced = (
-            "{protocol}://{username}:{password}@"
-            "{ip_address}:{port}/WLG_adv_dual_band2.htm").format(
-                protocol=self.ap_settings["protocol"],
-                username=self.ap_settings["admin_username"],
-                password=self.ap_settings["admin_password"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
-        self.networks = ["2G", "5G_1"]
-        self.channel_band_map = {
-            "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
-            "5G_1": [
-                36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120,
-                124, 128, 132, 136, 140, 149, 153, 157, 161, 165
-            ]
+            '{protocol}://{username}:{password}@'
+            '{ip_address}:{port}/WLG_adv_dual_band2.htm').format(
+                protocol=self.ap_settings['protocol'],
+                username=self.ap_settings['admin_username'],
+                password=self.ap_settings['admin_password'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
+        self.capabilities = {
+            'interfaces': ['2G', '5G_1'],
+            'channels': {
+                '2G': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+                '5G_1': [
+                    36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116,
+                    120, 124, 128, 132, 136, 140, 149, 153, 157, 161, 165
+                ]
+            },
+            'modes': {
+                '2G': ['VHT20', 'VHT40'],
+                '5G_1': ['VHT20', 'VHT40', 'VHT80']
+            },
+            'default_mode': 'VHT'
         }
+        for interface in self.capabilities['interfaces']:
+            self.ap_settings[interface] = {}
+
         self.region_map = {
-            "1": "Africa",
-            "2": "Asia",
-            "3": "Australia",
-            "4": "Canada",
-            "5": "Europe",
-            "6": "Israel",
-            "7": "Japan",
-            "8": "Korea",
-            "9": "Mexico",
-            "10": "South America",
-            "11": "United States",
-            "12": "Middle East(Algeria/Syria/Yemen)",
-            "14": "Russia",
-            "16": "China",
-            "17": "India",
-            "18": "Malaysia",
-            "19": "Middle East(Iran/Labanon/Qatar)",
-            "20": "Middle East(Turkey/Egypt/Tunisia/Kuwait)",
-            "21": "Middle East(Saudi Arabia)",
-            "22": "Middle East(United Arab Emirates)",
-            "23": "Singapore",
-            "24": "Taiwan"
+            '1': 'Africa',
+            '2': 'Asia',
+            '3': 'Australia',
+            '4': 'Canada',
+            '5': 'Europe',
+            '6': 'Israel',
+            '7': 'Japan',
+            '8': 'Korea',
+            '9': 'Mexico',
+            '10': 'South America',
+            '11': 'United States',
+            '12': 'Middle East(Algeria/Syria/Yemen)',
+            '14': 'Russia',
+            '16': 'China',
+            '17': 'India',
+            '18': 'Malaysia',
+            '19': 'Middle East(Iran/Labanon/Qatar)',
+            '20': 'Middle East(Turkey/Egypt/Tunisia/Kuwait)',
+            '21': 'Middle East(Saudi Arabia)',
+            '22': 'Middle East(United Arab Emirates)',
+            '23': 'Singapore',
+            '24': 'Taiwan'
         }
         self.config_page_fields = {
-            "region": "WRegion",
-            ("2G", "status"): "enable_ap",
-            ("5G_1", "status"): "enable_ap_an",
-            ("2G", "ssid"): "ssid",
-            ("5G_1", "ssid"): "ssid_an",
-            ("2G", "channel"): "w_channel",
-            ("5G_1", "channel"): "w_channel_an",
-            ("2G", "bandwidth"): "opmode",
-            ("5G_1", "bandwidth"): "opmode_an",
-            ("2G", "power"): "enable_tpc",
-            ("5G_1", "power"): "enable_tpc_an",
-            ("2G", "security_type"): "security_type",
-            ("5G_1", "security_type"): "security_type_an",
-            ("2G", "password"): "passphrase",
-            ("5G_1", "password"): "passphrase_an"
+            'region': 'WRegion',
+            ('2G', 'status'): 'enable_ap',
+            ('5G_1', 'status'): 'enable_ap_an',
+            ('2G', 'ssid'): 'ssid',
+            ('5G_1', 'ssid'): 'ssid_an',
+            ('2G', 'channel'): 'w_channel',
+            ('5G_1', 'channel'): 'w_channel_an',
+            ('2G', 'bandwidth'): 'opmode',
+            ('5G_1', 'bandwidth'): 'opmode_an',
+            ('2G', 'power'): 'enable_tpc',
+            ('5G_1', 'power'): 'enable_tpc_an',
+            ('2G', 'security_type'): 'security_type',
+            ('5G_1', 'security_type'): 'security_type_an',
+            ('2G', 'password'): 'passphrase',
+            ('5G_1', 'password'): 'passphrase_an'
         }
         self.bw_mode_values = {
-            "g and b": "11g",
-            "145Mbps": "VHT20",
-            "300Mbps": "VHT40",
-            "HT80": "VHT80"
+            'g and b': '11g',
+            '145Mbps': 'VHT20',
+            '300Mbps': 'VHT40',
+            'HT80': 'VHT80'
         }
         self.power_mode_values = {
-            "1": "100%",
-            "2": "75%",
-            "3": "50%",
-            "4": "25%"
+            '1': '100%',
+            '2': '75%',
+            '3': '50%',
+            '4': '25%'
         }
         self.bw_mode_text = {
-            "11g": "Up to 54 Mbps",
-            "VHT20": "Up to 289 Mbps",
-            "VHT40": "Up to 600 Mbps",
-            "VHT80": "Up to 1300 Mbps"
+            '11g': 'Up to 54 Mbps',
+            'VHT20': 'Up to 289 Mbps',
+            'VHT40': 'Up to 600 Mbps',
+            'VHT80': 'Up to 1300 Mbps'
         }
-        self.default_mode = 'VHT'
 
     def read_ap_settings(self):
         """Function to read ap settings."""
-        with BlockingBrowser(self.ap_settings["headless_browser"],
+        with BlockingBrowser(self.ap_settings['headless_browser'],
                              900) as browser:
             # 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:
+                if 'status' in key:
                     browser.visit_persistent(self.config_page_advanced,
                                              BROWSER_WAIT_MED, 10)
                     config_item = browser.find_by_name(value)
-                    self.ap_settings["{}_{}".format(key[1], key[0])] = int(
+                    self.ap_settings[key[0]][key[1]] = int(
                         config_item.first.checked)
                     browser.visit_persistent(self.config_page,
                                              BROWSER_WAIT_MED, 10)
                 else:
                     config_item = browser.find_by_name(value)
-                    if "bandwidth" in key:
-                        self.ap_settings["{}_{}".format(
-                            key[1], key[0])] = self.bw_mode_values[
-                                config_item.first.value]
-                    elif "power" in key:
-                        self.ap_settings["{}_{}".format(
-                            key[1], key[0])] = self.power_mode_values[
-                                config_item.first.value]
-                    elif "region" in key:
-                        self.ap_settings["region"] = self.region_map[
+                    if 'bandwidth' in key:
+                        self.ap_settings[key[0]][key[1]] = self.bw_mode_values[
                             config_item.first.value]
-                    elif "security_type" in key:
+                    elif 'power' in key:
+                        self.ap_settings[key[0]][
+                            key[1]] = self.power_mode_values[
+                                config_item.first.value]
+                    elif 'region' in 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["{}_{}".format(
-                                    key[1], key[0])] = item.value
+                                self.ap_settings[key[0]][key[1]] = item.value
                     else:
                         config_item = browser.find_by_name(value)
-                        self.ap_settings["{}_{}".format(
-                            key[1], key[0])] = config_item.first.value
+                        self.ap_settings[key[0]][
+                            key[1]] = config_item.first.value
         return self.ap_settings.copy()
 
     def configure_ap(self, **config_flags):
         """Function to configure ap wireless settings."""
         # Turn radios on or off
-        if config_flags["status_toggled"]:
+        if config_flags['status_toggled']:
             self.configure_radio_on_off()
         # Configure radios
-        with BlockingBrowser(self.ap_settings["headless_browser"],
+        with BlockingBrowser(self.ap_settings['headless_browser'],
                              900) as browser:
             # Visit URL
             browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
@@ -182,36 +188,31 @@
 
             # Update region, and power/bandwidth for each network
             config_item = browser.find_by_name(
-                self.config_page_fields["region"]).first
-            config_item.select_by_text(self.ap_settings["region"])
+                self.config_page_fields['region']).first
+            config_item.select_by_text(self.ap_settings['region'])
             for key, value in self.config_page_fields.items():
-                if "power" in key:
+                if 'power' in key:
                     config_item = browser.find_by_name(value).first
-                    config_item.select_by_text(self.ap_settings["{}_{}".format(
-                        key[1], key[0])])
-                elif "bandwidth" in key:
+                    config_item.select_by_text(
+                        self.ap_settings[key[0]][key[1]])
+                elif 'bandwidth' in key:
                     config_item = browser.find_by_name(value).first
                     try:
-                        config_item.select_by_text(
-                            self.bw_mode_text[self.ap_settings["{}_{}".format(
-                                key[1], key[0])]])
+                        config_item.select_by_text(self.bw_mode_text[
+                            self.ap_settings[key[0]][key[1]]])
                     except AttributeError:
                         self.log.warning(
-                            "Cannot select bandwidth. Keeping AP default.")
+                            '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["{}_{}".format(key[1],
-                                                               key[0])])
-                    if self.ap_settings["{}_{}".format(key[1],
-                                                       key[0])] == "WPA2-PSK":
+                if 'security_type' in key:
+                    browser.choose(value, self.ap_settings[key[0]][key[1]])
+                    if self.ap_settings[key[0]][key[1]] == 'WPA2-PSK':
                         config_item = browser.find_by_name(
                             self.config_page_fields[(key[0],
-                                                     "password")]).first
-                        config_item.fill(self.ap_settings["{}_{}".format(
-                            "password", key[0])])
+                                                     'password')]).first
+                        config_item.fill(self.ap_settings[key[0]]['password'])
 
             # Update SSID and channel for each network
             # NOTE: Update ordering done as such as workaround for R8000
@@ -219,19 +220,17 @@
             # variables are changed. However, region does have to be set before
             # channel in all cases.
             for key, value in self.config_page_fields.items():
-                if "ssid" in key:
+                if 'ssid' in key:
                     config_item = browser.find_by_name(value).first
-                    config_item.fill(self.ap_settings["{}_{}".format(
-                        key[1], key[0])])
-                elif "channel" in key:
+                    config_item.fill(self.ap_settings[key[0]][key[1]])
+                elif 'channel' in key:
                     config_item = browser.find_by_name(value).first
                     try:
-                        config_item.select(self.ap_settings["{}_{}".format(
-                            key[1], key[0])])
+                        config_item.select(self.ap_settings[key[0]][key[1]])
                         time.sleep(BROWSER_WAIT_SHORT)
                     except AttributeError:
                         self.log.warning(
-                            "Cannot select channel. Keeping AP default.")
+                            'Cannot select channel. Keeping AP default.')
                     try:
                         alert = browser.get_alert()
                         alert.accept()
@@ -239,7 +238,7 @@
                         pass
 
             time.sleep(BROWSER_WAIT_SHORT)
-            browser.find_by_name("Apply").first.click()
+            browser.find_by_name('Apply').first.click()
             time.sleep(BROWSER_WAIT_SHORT)
             try:
                 alert = browser.get_alert()
@@ -252,7 +251,7 @@
 
     def configure_radio_on_off(self):
         """Helper configuration function to turn radios on/off."""
-        with BlockingBrowser(self.ap_settings["headless_browser"],
+        with BlockingBrowser(self.ap_settings['headless_browser'],
                              900) as browser:
             # Visit URL
             browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
@@ -261,15 +260,15 @@
 
             # Turn radios on or off
             for key, value in self.config_page_fields.items():
-                if "status" in key:
+                if 'status' in key:
                     config_item = browser.find_by_name(value).first
-                    if self.ap_settings["{}_{}".format(key[1], key[0])]:
+                    if self.ap_settings[key[0]][key[1]]:
                         config_item.check()
                     else:
                         config_item.uncheck()
 
             time.sleep(BROWSER_WAIT_SHORT)
-            browser.find_by_name("Apply").first.click()
+            browser.find_by_name('Apply').first.click()
             time.sleep(BROWSER_WAIT_EXTRA_LONG)
             browser.visit_persistent(self.config_page, BROWSER_WAIT_EXTRA_LONG,
                                      10)
@@ -280,4 +279,4 @@
     def init_gui_data(self):
         """Function to initialize data used while interacting with web GUI"""
         super.init_gui_data()
-        self.region_map["11"] = "North America"
+        self.region_map['11'] = 'North America'
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 af8201d..284fda7 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
@@ -2,14 +2,14 @@
 #
 #   Copyright 2020 - The Android Open Source Project
 #
-#   Licensed under the Apache License, Version 2.0 (the "License");
+#   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,
+#   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.
@@ -32,92 +32,101 @@
         self.init_gui_data()
         # Read and update AP settings
         self.read_ap_settings()
-        if not set(ap_settings.items()).issubset(self.ap_settings.items()):
-            self.update_ap_settings(ap_settings)
+        self.update_ap_settings(ap_settings)
 
     def init_gui_data(self):
         """Function to initialize data used while interacting with web GUI"""
-        self.config_page = ("{protocol}://{username}:{password}@"
-                            "{ip_address}:{port}/index.htm").format(
-                                protocol=self.ap_settings["protocol"],
-                                username=self.ap_settings["admin_username"],
-                                password=self.ap_settings["admin_password"],
-                                ip_address=self.ap_settings["ip_address"],
-                                port=self.ap_settings["port"])
+        self.config_page = ('{protocol}://{username}:{password}@'
+                            '{ip_address}:{port}/index.htm').format(
+                                protocol=self.ap_settings['protocol'],
+                                username=self.ap_settings['admin_username'],
+                                password=self.ap_settings['admin_password'],
+                                ip_address=self.ap_settings['ip_address'],
+                                port=self.ap_settings['port'])
         self.config_page_advanced = (
-            "{protocol}://{username}:{password}@"
-            "{ip_address}:{port}/adv_index.htm").format(
-                protocol=self.ap_settings["protocol"],
-                username=self.ap_settings["admin_username"],
-                password=self.ap_settings["admin_password"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
-        self.networks = ["2G", "5G_1"]
-        self.channel_band_map = {
-            "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
-            "5G_1": [
-                36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120,
-                124, 128, 132, 136, 140, 149, 153, 157, 161, 165
-            ]
+            '{protocol}://{username}:{password}@'
+            '{ip_address}:{port}/adv_index.htm').format(
+                protocol=self.ap_settings['protocol'],
+                username=self.ap_settings['admin_username'],
+                password=self.ap_settings['admin_password'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
+        self.capabilities = {
+            'interfaces': ['2G', '5G_1'],
+            'channels': {
+                '2G': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+                '5G_1': [
+                    36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116,
+                    120, 124, 128, 132, 136, 140, 149, 153, 157, 161, 165
+                ]
+            },
+            'modes': {
+                '2G': ['VHT20', 'VHT40'],
+                '5G_1': ['VHT20', 'VHT40', 'VHT80']
+            },
+            'default_mode': 'VHT'
         }
+        for interface in self.capabilities['interfaces']:
+            self.ap_settings[interface] = {}
+
         self.config_page_fields = {
-            "region": "WRegion",
-            ("2G", "status"): "enable_ap",
-            ("5G_1", "status"): "enable_ap_an",
-            ("2G", "ssid"): "ssid",
-            ("5G_1", "ssid"): "ssid_an",
-            ("2G", "channel"): "w_channel",
-            ("5G_1", "channel"): "w_channel_an",
-            ("2G", "bandwidth"): "opmode",
-            ("5G_1", "bandwidth"): "opmode_an",
-            ("2G", "security_type"): "security_type",
-            ("5G_1", "security_type"): "security_type_an",
-            ("2G", "password"): "passphrase",
-            ("5G_1", "password"): "passphrase_an"
+            'region': 'WRegion',
+            ('2G', 'status'): 'enable_ap',
+            ('5G_1', 'status'): 'enable_ap_an',
+            ('2G', 'ssid'): 'ssid',
+            ('5G_1', 'ssid'): 'ssid_an',
+            ('2G', 'channel'): 'w_channel',
+            ('5G_1', 'channel'): 'w_channel_an',
+            ('2G', 'bandwidth'): 'opmode',
+            ('5G_1', 'bandwidth'): 'opmode_an',
+            ('2G', 'security_type'): 'security_type',
+            ('5G_1', 'security_type'): 'security_type_an',
+            ('2G', 'password'): 'passphrase',
+            ('5G_1', 'password'): 'passphrase_an'
         }
         self.region_map = {
-            "0": "Africa",
-            "1": "Asia",
-            "2": "Australia",
-            "3": "Canada",
-            "4": "Europe",
-            "5": "Israel",
-            "6": "Japan",
-            "7": "Korea",
-            "8": "Mexico",
-            "9": "South America",
-            "10": "United States",
-            "11": "China",
-            "12": "India",
-            "13": "Malaysia",
-            "14": "Middle East(Algeria/Syria/Yemen)",
-            "15": "Middle East(Iran/Labanon/Qatar)",
-            "16": "Middle East(Turkey/Egypt/Tunisia/Kuwait)",
-            "17": "Middle East(Saudi Arabia)",
-            "18": "Middle East(United Arab Emirates)",
-            "19": "Russia",
-            "20": "Singapore",
-            "21": "Taiwan"
+            '0': 'Africa',
+            '1': 'Asia',
+            '2': 'Australia',
+            '3': 'Canada',
+            '4': 'Europe',
+            '5': 'Israel',
+            '6': 'Japan',
+            '7': 'Korea',
+            '8': 'Mexico',
+            '9': 'South America',
+            '10': 'United States',
+            '11': 'China',
+            '12': 'India',
+            '13': 'Malaysia',
+            '14': 'Middle East(Algeria/Syria/Yemen)',
+            '15': 'Middle East(Iran/Labanon/Qatar)',
+            '16': 'Middle East(Turkey/Egypt/Tunisia/Kuwait)',
+            '17': 'Middle East(Saudi Arabia)',
+            '18': 'Middle East(United Arab Emirates)',
+            '19': 'Russia',
+            '20': 'Singapore',
+            '21': 'Taiwan'
         }
         self.bw_mode_text = {
             '2G': {
-                "11g": "Up to 54 Mbps",
-                "VHT20": "Up to 289 Mbps",
-                "VHT40": "Up to 600 Mbps"
+                '11g': 'Up to 54 Mbps',
+                'VHT20': 'Up to 289 Mbps',
+                'VHT40': 'Up to 600 Mbps'
             },
             '5G_1': {
-                "VHT20": "Up to 347 Mbps",
-                "VHT40": "Up to 800 Mbps",
-                "VHT80": "Up to 1733 Mbps"
+                'VHT20': 'Up to 347 Mbps',
+                'VHT40': 'Up to 800 Mbps',
+                'VHT80': 'Up to 1733 Mbps'
             }
         }
         self.bw_mode_values = {
-            "1": "11g",
-            "2": "VHT20",
-            "3": "VHT40",
-            "7": "VHT20",
-            "8": "VHT40",
-            "9": "VHT80"
+            '1': '11g',
+            '2': 'VHT20',
+            '3': 'VHT40',
+            '7': 'VHT20',
+            '8': 'VHT40',
+            '9': 'VHT80'
         }
         self.security_mode_values = {
             '2G': {
@@ -129,7 +138,6 @@
                 'WPA2-PSK': 'security_an_wpa2'
             }
         }
-        self.default_mode = 'VHT'
 
     def read_ap_settings(self):
         """Function to read ap wireless settings."""
@@ -137,43 +145,41 @@
         self.read_radio_on_off()
         # Get radio configuration. Note that if both radios are off, the below
         # code will result in an error
-        with BlockingBrowser(self.ap_settings["headless_browser"],
+        with BlockingBrowser(self.ap_settings['headless_browser'],
                              900) as browser:
             browser.visit_persistent(self.config_page,
                                      BROWSER_WAIT_MED,
                                      10,
-                                     check_for_element="wireless")
-            wireless_button = browser.find_by_id("wireless").first
+                                     check_for_element='wireless')
+            wireless_button = browser.find_by_id('wireless').first
             wireless_button.click()
             time.sleep(BROWSER_WAIT_MED)
 
-            with browser.get_iframe("formframe") as iframe:
+            with browser.get_iframe('formframe') as iframe:
                 for key, value in self.config_page_fields.items():
-                    if "bandwidth" in key:
+                    if 'bandwidth' in key:
                         config_item = iframe.find_by_name(value).first
-                        self.ap_settings["{}_{}".format(
-                            key[1],
-                            key[0])] = self.bw_mode_values[config_item.value]
-                    elif "region" in key:
-                        config_item = iframe.find_by_name(value).first
-                        self.ap_settings["region"] = self.region_map[
+                        self.ap_settings[key[0]][key[1]] = self.bw_mode_values[
                             config_item.value]
-                    elif "password" in key:
+                    elif 'region' in key:
+                        config_item = iframe.find_by_name(value).first
+                        self.ap_settings['region'] = self.region_map[
+                            config_item.value]
+                    elif 'password' in key:
                         try:
                             config_item = iframe.find_by_name(value).first
-                            self.ap_settings["{}_{}".format(
-                                key[1], key[0])] = config_item.value
-                            self.ap_settings["{}_{}".format(
-                                "security_type", key[0])] = "WPA2-PSK"
+                            self.ap_settings[key[0]][
+                                key[1]] = config_item.value
+                            self.ap_settings[
+                                key[0]]['security_type'] = 'WPA2-PSK'
                         except:
-                            self.ap_settings["{}_{}".format(
-                                key[1], key[0])] = "defaultpassword"
-                            self.ap_settings["{}_{}".format(
-                                "security_type", key[0])] = "Disable"
-                    elif ("channel" in key) or ("ssid" in key):
+                            self.ap_settings[key[0]][
+                                key[1]] = 'defaultpassword'
+                            self.ap_settings[
+                                key[0]]['security_type'] = 'Disable'
+                    elif ('channel' in key) or ('ssid' in key):
                         config_item = iframe.find_by_name(value).first
-                        self.ap_settings["{}_{}".format(
-                            key[1], key[0])] = config_item.value
+                        self.ap_settings[key[0]][key[1]] = config_item.value
                     else:
                         pass
         return self.ap_settings.copy()
@@ -181,72 +187,71 @@
     def configure_ap(self, **config_flags):
         """Function to configure ap wireless settings."""
         # Turn radios on or off
-        if config_flags["status_toggled"]:
+        if config_flags['status_toggled']:
             self.configure_radio_on_off()
         # Configure radios
-        with BlockingBrowser(self.ap_settings["headless_browser"],
+        with BlockingBrowser(self.ap_settings['headless_browser'],
                              900) as browser:
             browser.visit_persistent(self.config_page,
                                      BROWSER_WAIT_MED,
                                      10,
-                                     check_for_element="wireless")
-            wireless_button = browser.find_by_id("wireless").first
+                                     check_for_element='wireless')
+            wireless_button = browser.find_by_id('wireless').first
             wireless_button.click()
             time.sleep(BROWSER_WAIT_MED)
 
-            with browser.get_iframe("formframe") as iframe:
+            with browser.get_iframe('formframe') as iframe:
                 # Update AP region. Must be done before channel setting
                 config_item = iframe.find_by_name(
-                    self.config_page_fields["region"]).first
-                config_item.select_by_text(self.ap_settings["region"])
+                    self.config_page_fields['region']).first
+                config_item.select_by_text(self.ap_settings['region'])
                 # Update wireless settings for each network
                 for key, value in self.config_page_fields.items():
-                    if "ssid" in key:
+                    if 'ssid' in key:
                         config_item = iframe.find_by_name(value).first
-                        config_item.fill(self.ap_settings["{}_{}".format(
-                            key[1], key[0])])
-                    elif "channel" in key:
-                        channel_string = "0" * (int(self.ap_settings[
-                            "{}_{}".format(key[1], key[0])]) < 10) + str(
-                                self.ap_settings["{}_{}".format(
-                                    key[1], key[0])]) + "(DFS)" * (48 < int(
-                                        self.ap_settings["{}_{}".format(
-                                            key[1], key[0])]) < 149)
+                        config_item.fill(self.ap_settings[key[0]][key[1]])
+                    elif 'channel' in key:
+                        channel = self.ap_settings[key[0]][key[1]]
+                        if int(channel) < 10:
+                            channel_string = '0' + str(channel)
+                        elif int(channel) > 48 and int(channel) < 149:
+                            channel_string = str(channel) + 'DFS'
+                        else:
+                            channel_string = str(channel)
                         config_item = iframe.find_by_name(value).first
                         try:
                             config_item.select_by_text(channel_string)
                         except AttributeError:
                             self.log.warning(
-                                "Cannot select channel. Keeping AP default.")
-                    elif "bandwidth" in key:
+                                'Cannot select channel. Keeping AP default.')
+                    elif 'bandwidth' in key:
                         config_item = iframe.find_by_name(value).first
                         try:
                             config_item.select_by_text(
                                 str(self.bw_mode_text[key[0]][self.ap_settings[
-                                    "{}_{}".format(key[1], key[0])]]))
+                                    key[0]][key[1]]]))
                         except AttributeError:
                             self.log.warning(
-                                "Cannot select bandwidth. Keeping AP default.")
+                                'Cannot select bandwidth. Keeping AP default.')
                 # Update passwords for WPA2-PSK protected networks
                 # (Must be done after security type is selected)
                 for key, value in self.config_page_fields.items():
-                    if "security_type" in key:
+                    if 'security_type' in key:
                         security_option = browser.driver.find_element_by_id(
                             self.security_mode_values[key[0]][self.ap_settings[
-                                "{}_{}".format(key[1], key[0])]])
+                                key[0]][key[1]]])
                         action = selenium.webdriver.common.action_chains.ActionChains(
                             browser.driver)
                         action.move_to_element(
                             security_option).click().perform()
-                        if self.ap_settings["{}_{}".format(
-                                key[1], key[0])] == "WPA2-PSK":
+                        if self.ap_settings[key[0]][key[1]] == 'WPA2-PSK':
                             config_item = iframe.find_by_name(
                                 self.config_page_fields[(key[0],
-                                                         "password")]).first
-                            config_item.fill(self.ap_settings["{}_{}".format(
-                                "password", key[0])])
+                                                         'password')]).first
+                            config_item.fill(
+                                self.ap_settings[key[0]]['password'])
 
-                apply_button = iframe.find_by_name("Apply")
+                apply_button = iframe.find_by_name('Apply')
                 apply_button[0].click()
                 time.sleep(BROWSER_WAIT_SHORT)
                 try:
@@ -267,56 +272,56 @@
 
     def configure_radio_on_off(self):
         """Helper configuration function to turn radios on/off."""
-        with BlockingBrowser(self.ap_settings["headless_browser"],
+        with BlockingBrowser(self.ap_settings['headless_browser'],
                              900) as browser:
             browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
             browser.visit_persistent(self.config_page_advanced,
                                      BROWSER_WAIT_MED,
                                      10,
-                                     check_for_element="advanced_bt")
-            advanced_button = browser.find_by_id("advanced_bt").first
+                                     check_for_element='advanced_bt')
+            advanced_button = browser.find_by_id('advanced_bt').first
             advanced_button.click()
             time.sleep(BROWSER_WAIT_MED)
-            wireless_button = browser.find_by_id("wladv").first
+            wireless_button = browser.find_by_id('wladv').first
             wireless_button.click()
             time.sleep(BROWSER_WAIT_MED)
 
-            with browser.get_iframe("formframe") as iframe:
+            with browser.get_iframe('formframe') as iframe:
                 # Turn radios on or off
                 for key, value in self.config_page_fields.items():
-                    if "status" in key:
+                    if 'status' in key:
                         config_item = iframe.find_by_name(value).first
-                        if self.ap_settings["{}_{}".format(key[1], key[0])]:
+                        if self.ap_settings[key[0]][key[1]]:
                             config_item.check()
                         else:
                             config_item.uncheck()
 
                 time.sleep(BROWSER_WAIT_SHORT)
-                browser.find_by_name("Apply").first.click()
+                browser.find_by_name('Apply').first.click()
                 time.sleep(BROWSER_WAIT_EXTRA_LONG)
                 browser.visit_persistent(self.config_page,
                                          BROWSER_WAIT_EXTRA_LONG, 10)
 
     def read_radio_on_off(self):
         """Helper configuration function to read radio status."""
-        with BlockingBrowser(self.ap_settings["headless_browser"],
+        with BlockingBrowser(self.ap_settings['headless_browser'],
                              900) as browser:
             browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
             browser.visit_persistent(self.config_page_advanced,
                                      BROWSER_WAIT_MED,
                                      10,
-                                     check_for_element="advanced_bt")
-            advanced_button = browser.find_by_id("advanced_bt").first
+                                     check_for_element='advanced_bt')
+            advanced_button = browser.find_by_id('advanced_bt').first
             advanced_button.click()
             time.sleep(BROWSER_WAIT_SHORT)
-            wireless_button = browser.find_by_id("wladv").first
+            wireless_button = browser.find_by_id('wladv').first
             wireless_button.click()
             time.sleep(BROWSER_WAIT_MED)
 
-            with browser.get_iframe("formframe") as iframe:
+            with browser.get_iframe('formframe') as iframe:
                 # Turn radios on or off
                 for key, value in self.config_page_fields.items():
-                    if "status" in key:
+                    if 'status' in key:
                         config_item = iframe.find_by_name(value).first
-                        self.ap_settings["{}_{}".format(key[1], key[0])] = int(
+                        self.ap_settings[key[0]][key[1]] = int(
                             config_item.checked)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7800.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7800.py
index fdb85f6..e06a3e3 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7800.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r7800.py
@@ -2,14 +2,14 @@
 #
 #   Copyright 2020 - The Android Open Source Project
 #
-#   Licensed under the Apache License, Version 2.0 (the "License");
+#   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,
+#   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.
@@ -23,12 +23,7 @@
     Since most of the class' implementation is shared with the R7500, this
     class inherits from NetgearR7500AP and simply redefines config parameters
     """
-    def __init__(self, ap_settings):
-        super().__init__(ap_settings)
-        self.init_gui_data()
+    def init_gui_data(self):
+        super().init_gui_data()
         # Overwrite minor differences from R7500 AP
-        self.bw_mode_text_2g["VHT20"] = "Up to 347 Mbps"
-        # Read and update AP settings
-        self.read_ap_settings()
-        if not set(ap_settings.items()).issubset(self.ap_settings.items()):
-            self.update_ap_settings(ap_settings)
+        self.bw_mode_text_2g['VHT20'] = 'Up to 347 Mbps'
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r8000.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r8000.py
index 11fb1c1..0b9cbf9 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r8000.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r8000.py
@@ -2,14 +2,14 @@
 #
 #   Copyright 2020 - The Android Open Source Project
 #
-#   Licensed under the Apache License, Version 2.0 (the "License");
+#   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,
+#   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.
@@ -27,52 +27,62 @@
         super().init_gui_data()
         # Overwrite minor differences from R7000 AP
         self.config_page = (
-            "{protocol}://{username}:{password}@"
-            "{ip_address}:{port}/WLG_wireless_dual_band_r8000.htm").format(
-                protocol=self.ap_settings["protocol"],
-                username=self.ap_settings["admin_username"],
-                password=self.ap_settings["admin_password"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
+            '{protocol}://{username}:{password}@'
+            '{ip_address}:{port}/WLG_wireless_dual_band_r8000.htm').format(
+                protocol=self.ap_settings['protocol'],
+                username=self.ap_settings['admin_username'],
+                password=self.ap_settings['admin_password'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
         self.config_page_nologin = (
-            "{protocol}://{ip_address}:{port}/"
-            "WLG_wireless_dual_band_r8000.htm").format(
-                protocol=self.ap_settings["protocol"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
+            '{protocol}://{ip_address}:{port}/'
+            'WLG_wireless_dual_band_r8000.htm').format(
+                protocol=self.ap_settings['protocol'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
         self.config_page_advanced = (
-            "{protocol}://{username}:{password}@"
-            "{ip_address}:{port}/WLG_adv_dual_band2_r8000.htm").format(
-                protocol=self.ap_settings["protocol"],
-                username=self.ap_settings["admin_username"],
-                password=self.ap_settings["admin_password"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
-        self.networks = ["2G", "5G_1", "5G_2"]
-        self.channel_band_map = {
-            "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
-            "5G_1": [36, 40, 44, 48],
-            "5G_2": [149, 153, 157, 161, 165]
+            '{protocol}://{username}:{password}@'
+            '{ip_address}:{port}/WLG_adv_dual_band2_r8000.htm').format(
+                protocol=self.ap_settings['protocol'],
+                username=self.ap_settings['admin_username'],
+                password=self.ap_settings['admin_password'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
+        self.capabilities = {
+            'interfaces': ['2G', '5G_1', '5G_2'],
+            'channels': {
+                '2G': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+                '5G_1': [36, 40, 44, 48],
+                '5G_2': [149, 153, 157, 161, 165]
+            },
+            'modes': {
+                '2G': ['VHT20', 'VHT40'],
+                '5G_1': ['VHT20', 'VHT40', 'VHT80'],
+                '5G_2': ['VHT20', 'VHT40', 'VHT80']
+            },
+            'default_mode': 'VHT'
         }
+        for interface in self.capabilities['interfaces']:
+            self.ap_settings[interface] = {}
+
         self.config_page_fields = {
-            "region": "WRegion",
-            ("2G", "status"): "enable_ap",
-            ("5G_1", "status"): "enable_ap_an",
-            ("5G_2", "status"): "enable_ap_an_2",
-            ("2G", "ssid"): "ssid",
-            ("5G_1", "ssid"): "ssid_an",
-            ("5G_2", "ssid"): "ssid_an_2",
-            ("2G", "channel"): "w_channel",
-            ("5G_1", "channel"): "w_channel_an",
-            ("5G_2", "channel"): "w_channel_an_2",
-            ("2G", "bandwidth"): "opmode",
-            ("5G_1", "bandwidth"): "opmode_an",
-            ("5G_2", "bandwidth"): "opmode_an_2",
-            ("2G", "security_type"): "security_type",
-            ("5G_1", "security_type"): "security_type_an",
-            ("5G_2", "security_type"): "security_type_an_2",
-            ("2G", "password"): "passphrase",
-            ("5G_1", "password"): "passphrase_an",
-            ("5G_2", "password"): "passphrase_an_2"
+            'region': 'WRegion',
+            ('2G', 'status'): 'enable_ap',
+            ('5G_1', 'status'): 'enable_ap_an',
+            ('5G_2', 'status'): 'enable_ap_an_2',
+            ('2G', 'ssid'): 'ssid',
+            ('5G_1', 'ssid'): 'ssid_an',
+            ('5G_2', 'ssid'): 'ssid_an_2',
+            ('2G', 'channel'): 'w_channel',
+            ('5G_1', 'channel'): 'w_channel_an',
+            ('5G_2', 'channel'): 'w_channel_an_2',
+            ('2G', 'bandwidth'): 'opmode',
+            ('5G_1', 'bandwidth'): 'opmode_an',
+            ('5G_2', 'bandwidth'): 'opmode_an_2',
+            ('2G', 'security_type'): 'security_type',
+            ('5G_1', 'security_type'): 'security_type_an',
+            ('5G_2', 'security_type'): 'security_type_an_2',
+            ('2G', 'password'): 'passphrase',
+            ('5G_1', 'password'): 'passphrase_an',
+            ('5G_2', 'password'): 'passphrase_an_2'
         }
-        self.default_mode = 'VHT'
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r8500.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r8500.py
deleted file mode 100644
index 6983ec6..0000000
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_r8500.py
+++ /dev/null
@@ -1,89 +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_contrib.test_utils.wifi.wifi_retail_ap import NetgearR7000AP
-
-
-class NetgearR8500AP(NetgearR7000AP):
-    """Class that implements Netgear R8500 AP.
-
-    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()
-        # Overwrite minor differences from R8000 AP
-        self.config_page = (
-            "{protocol}://{username}:{password}@"
-            "{ip_address}:{port}/WLG_wireless_tri_band.htm").format(
-                protocol=self.ap_settings["protocol"],
-                username=self.ap_settings["admin_username"],
-                password=self.ap_settings["admin_password"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
-        self.config_page_nologin = (
-            "{protocol}://{ip_address}:{port}/"
-            "WLG_wireless_tri_band.htm").format(
-                protocol=self.ap_settings["protocol"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
-        self.config_page_advanced = (
-            "{protocol}://{username}:{password}@"
-            "{ip_address}:{port}/WLG_adv_tri_band2.htm").format(
-                protocol=self.ap_settings["protocol"],
-                username=self.ap_settings["admin_username"],
-                password=self.ap_settings["admin_password"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
-        self.networks = ["2G", "5G_1", "5G_2"]
-        self.channel_band_map = {
-            "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
-            "5G_1": [36, 40, 44, 48],
-            "5G_2": [149, 153, 157, 161, 165]
-        }
-        self.config_page_fields = {
-            "region": "WRegion",
-            ("2G", "status"): "enable_ap",
-            ("5G_1", "status"): "enable_ap_an",
-            ("5G_2", "status"): "enable_ap_an_2",
-            ("2G", "ssid"): "ssid",
-            ("5G_1", "ssid"): "ssid_an",
-            ("5G_2", "ssid"): "ssid_an_2",
-            ("2G", "channel"): "w_channel",
-            ("5G_1", "channel"): "w_channel_an",
-            ("5G_2", "channel"): "w_channel_an_2",
-            ("2G", "bandwidth"): "opmode",
-            ("5G_1", "bandwidth"): "opmode_an",
-            ("5G_2", "bandwidth"): "opmode_an_2",
-            ("2G", "security_type"): "security_type",
-            ("5G_1", "security_type"): "security_type_an",
-            ("5G_2", "security_type"): "security_type_an_2",
-            ("2G", "password"): "passphrase",
-            ("5G_1", "password"): "passphrase_an",
-            ("5G_2", "password"): "passphrase_an_2"
-        }
-        self.bw_mode_text = {
-            "11g": "Up to 54 Mbps",
-            "VHT20": "Up to 433 Mbps",
-            "VHT40": "Up to 1000 Mbps",
-            "VHT80": "Up to 2165 Mbps"
-        }
-        self.default_mode = 'HE'
-        # Read and update AP settings
-        self.read_ap_settings()
-        if not set(ap_settings.items()).issubset(self.ap_settings.items()):
-            self.update_ap_settings(ap_settings)
diff --git a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax120.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax120.py
index b0f6859..e718ebd 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax120.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax120.py
@@ -2,14 +2,14 @@
 #
 #   Copyright 2020 - The Android Open Source Project
 #
-#   Licensed under the Apache License, Version 2.0 (the "License");
+#   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,
+#   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.
@@ -31,92 +31,97 @@
     Since most of the class' implementation is shared with the R7500, this
     class inherits from NetgearR7500AP and simply redefines config parameters
     """
-    def __init__(self, ap_settings):
-        super().__init__(ap_settings)
-        self.init_gui_data()
-        # Read and update AP settings
-        self.read_ap_settings()
-        if not set(ap_settings.items()).issubset(self.ap_settings.items()):
-            self.update_ap_settings(ap_settings)
-
     def init_gui_data(self):
         """Function to initialize data used while interacting with web GUI"""
-        self.config_page = ("{protocol}://{username}:{password}@"
-                            "{ip_address}:{port}/index.htm").format(
-                                protocol=self.ap_settings["protocol"],
-                                username=self.ap_settings["admin_username"],
-                                password=self.ap_settings["admin_password"],
-                                ip_address=self.ap_settings["ip_address"],
-                                port=self.ap_settings["port"])
+        self.config_page = ('{protocol}://{username}:{password}@'
+                            '{ip_address}:{port}/index.htm').format(
+                                protocol=self.ap_settings['protocol'],
+                                username=self.ap_settings['admin_username'],
+                                password=self.ap_settings['admin_password'],
+                                ip_address=self.ap_settings['ip_address'],
+                                port=self.ap_settings['port'])
         self.config_page_advanced = (
-            "{protocol}://{username}:{password}@"
-            "{ip_address}:{port}/adv_index.htm").format(
-                protocol=self.ap_settings["protocol"],
-                username=self.ap_settings["admin_username"],
-                password=self.ap_settings["admin_password"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
-        self.networks = ["2G", "5G_1"]
-        self.channel_band_map = {
-            "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
-            "5G_1": [
-                36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120,
-                124, 128, 132, 136, 140, 149, 153, 157, 161, 165
-            ]
+            '{protocol}://{username}:{password}@'
+            '{ip_address}:{port}/adv_index.htm').format(
+                protocol=self.ap_settings['protocol'],
+                username=self.ap_settings['admin_username'],
+                password=self.ap_settings['admin_password'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
+        self.capabilities = {
+            'interfaces': ['2G', '5G_1'],
+            'channels': {
+                '2G': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+                '5G_1': [
+                    36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116,
+                    120, 124, 128, 132, 136, 140, 149, 153, 157, 161, 165
+                ]
+            },
+            'modes': {
+                '2G': ['VHT20', 'VHT40', 'HE20', 'HE40'],
+                '5G_1': [
+                    'VHT20', 'VHT40', 'VHT80', 'VHT160', 'HE20', 'HE40',
+                    'HE80', 'HE160'
+                ]
+            },
+            'default_mode': 'HE'
         }
+        for interface in self.capabilities['interfaces']:
+            self.ap_settings[interface] = {}
+
         self.config_page_fields = {
-            "region": "WRegion",
-            "enable_ax": "enable_ax_chec",
-            ("2G", "status"): "enable_ap",
-            ("5G_1", "status"): "enable_ap_an",
-            ("2G", "ssid"): "ssid",
-            ("5G_1", "ssid"): "ssid_an",
-            ("2G", "channel"): "w_channel",
-            ("5G_1", "channel"): "w_channel_an",
-            ("2G", "bandwidth"): "opmode",
-            ("5G_1", "bandwidth"): "opmode_an",
-            ("2G", "security_type"): "security_type",
-            ("5G_1", "security_type"): "security_type_an",
-            ("2G", "password"): "passphrase",
-            ("5G_1", "password"): "passphrase_an"
+            'region': 'WRegion',
+            'enable_ax': 'enable_ax_chec',
+            ('2G', 'status'): 'enable_ap',
+            ('5G_1', 'status'): 'enable_ap_an',
+            ('2G', 'ssid'): 'ssid',
+            ('5G_1', 'ssid'): 'ssid_an',
+            ('2G', 'channel'): 'w_channel',
+            ('5G_1', 'channel'): 'w_channel_an',
+            ('2G', 'bandwidth'): 'opmode',
+            ('5G_1', 'bandwidth'): 'opmode_an',
+            ('2G', 'security_type'): 'security_type',
+            ('5G_1', 'security_type'): 'security_type_an',
+            ('2G', 'password'): 'passphrase',
+            ('5G_1', 'password'): 'passphrase_an'
         }
         self.region_map = {
-            "0": "Africa",
-            "1": "Asia",
-            "2": "Australia",
-            "3": "Canada",
-            "4": "Europe",
-            "5": "Israel",
-            "6": "Japan",
-            "7": "Korea",
-            "8": "Mexico",
-            "9": "South America",
-            "10": "United States",
-            "11": "China",
-            "12": "India",
-            "13": "Malaysia",
-            "14": "Middle East(Algeria/Syria/Yemen)",
-            "15": "Middle East(Iran/Labanon/Qatar)",
-            "16": "Middle East(Egypt/Tunisia/Kuwait)",
-            "17": "Middle East(Turkey)",
-            "18": "Middle East(Saudi Arabia/United Arab Emirates)",
-            "19": "Russia",
-            "20": "Singapore",
-            "21": "Taiwan",
+            '0': 'Africa',
+            '1': 'Asia',
+            '2': 'Australia',
+            '3': 'Canada',
+            '4': 'Europe',
+            '5': 'Israel',
+            '6': 'Japan',
+            '7': 'Korea',
+            '8': 'Mexico',
+            '9': 'South America',
+            '10': 'United States',
+            '11': 'China',
+            '12': 'India',
+            '13': 'Malaysia',
+            '14': 'Middle East(Algeria/Syria/Yemen)',
+            '15': 'Middle East(Iran/Labanon/Qatar)',
+            '16': 'Middle East(Egypt/Tunisia/Kuwait)',
+            '17': 'Middle East(Turkey)',
+            '18': 'Middle East(Saudi Arabia/United Arab Emirates)',
+            '19': 'Russia',
+            '20': 'Singapore',
+            '21': 'Taiwan',
         }
         self.bw_mode_text = {
             '2G': {
-                "11g": "Up to 54 Mbps (11g)",
-                "HE20": "Up to 573.5 Mbps (11ax, HT20, 1024-QAM)",
-                "HE40": "Up to 1147 Mbps (11ax, HT40, 1024-QAM)",
+                '11g': 'Up to 54 Mbps (11g)',
+                'HE20': 'Up to 573.5 Mbps (11ax, HT20, 1024-QAM)',
+                'HE40': 'Up to 1147 Mbps (11ax, HT40, 1024-QAM)',
                 'VHT20': 'Up to 481 Mbps (11ng, HT20, 1024-QAM)',
                 'VHT40': 'Up to 1000 Mbps (11ng, HT40, 1024-QAM)'
             },
             '5G_1': {
-                "HE20": "Up to 1147 Mbps (11ax, HT20, 1024-QAM)",
-                "HE40": "Up to 2294 Mbps (11ax, HT40, 1024-QAM)",
-                "HE80": "Up to 4803 Mbps (11ax, HT80, 1024-QAM)",
-                "HE160": "Up to 4803 Mbps (11ax, HT160, 1024-QAM)",
+                'HE20': 'Up to 1147 Mbps (11ax, HT20, 1024-QAM)',
+                'HE40': 'Up to 2294 Mbps (11ax, HT40, 1024-QAM)',
+                'HE80': 'Up to 4803 Mbps (11ax, HT80, 1024-QAM)',
+                'HE160': 'Up to 4803 Mbps (11ax, HT160, 1024-QAM)',
                 'VHT20': 'Up to 962 Mbps (11ac, HT20, 1024-QAM)',
                 'VHT40': 'Up to 2000 Mbps (11ac, HT40, 1024-QAM)',
                 'VHT80': 'Up to 4333 Mbps (11ac, HT80, 1024-QAM)',
@@ -124,19 +129,20 @@
             }
         }
         self.bw_mode_values = {
+            # first key is a boolean indicating if 11ax is enabled
             0: {
-                "1": "11g",
-                "2": "VHT20",
-                "3": "VHT40",
+                '1': '11g',
+                '2': 'VHT20',
+                '3': 'VHT40',
                 '7': 'VHT20',
                 '8': 'VHT40',
                 '9': 'VHT80',
                 '10': 'VHT160'
             },
             1: {
-                "1": "11g",
-                "2": "HE20",
-                "3": "HE40",
+                '1': '11g',
+                '2': 'HE20',
+                '3': 'HE40',
                 '7': 'HE20',
                 '8': 'HE40',
                 '9': 'HE80',
@@ -153,7 +159,6 @@
                 'WPA2-PSK': 'security_an_wpa2'
             }
         }
-        self.default_mode = 'HE'
 
     def set_bandwidth(self, network, bandwidth):
         """Function that sets network bandwidth/mode.
@@ -162,31 +167,30 @@
             network: string containing network identifier (2G, 5G_1, 5G_2)
             bandwidth: string containing mode, e.g. 11g, VHT20, VHT40, VHT80.
         """
-        if 'BW' in bandwidth:
-            bandwidth = bandwidth.replace('BW', self.default_mode)
-        setting_to_update = {"bandwidth_{}".format(network): str(bandwidth)}
+        if 'bw' in bandwidth:
+            bandwidth = bandwidth.replace('bw',
+                                          self.capabilities['default_mode'])
+        if bandwidth not in self.capabilities['modes'][network]:
+            self.log.error('{} mode is not supported on {} interface.'.format(
+                bandwidth, network))
+        setting_to_update = {network: {'bandwidth': str(bandwidth)}}
+        setting_to_update['enable_ax'] = int('HE' in bandwidth)
         # Check if other interfaces need to be changed too
-        requested_mode = ''.join([x for x in bandwidth if x.isalpha()])
-        setting_to_update["enable_ax"] = int('HE' in requested_mode)
-        for curr_network in self.networks:
-            if curr_network == network:
-                pass
-            else:
-                curr_mode = ''.join([
-                    x for x in self.ap_settings["bandwidth_{}".format(
-                        curr_network)] if x.isalpha()
-                ])
-                curr_bw = ''.join([
-                    x for x in self.ap_settings["bandwidth_{}".format(
-                        curr_network)] if x.isdigit()
-                ])
-                if curr_mode != requested_mode:
-                    updated_mode = '{}{}'.format(requested_mode, curr_bw)
-                    self.log.warning("All networks must be VHT or HE. "
-                                     "Updating {} to {}".format(
-                                         curr_network, updated_mode))
-                    setting_to_update["bandwidth_{}".format(
-                        curr_network)] = updated_mode
+        requested_mode = 'HE' if 'HE' in bandwidth else 'VHT'
+        other_network = '2G' if '5G_1' in network else '5G_1'
+        other_mode = 'HE' if 'HE' in self.ap_settings[other_network][
+            'bandwidth'] else 'VHT'
+        other_bw = ''.join([
+            x for x in self.ap_settings[other_network]['bandwidth']
+            if x.isdigit()
+        ])
+        if other_mode != requested_mode:
+            updated_mode = '{}{}'.format(requested_mode, other_bw)
+            self.log.warning('All networks must be VHT or HE. '
+                             'Updating {} to {}'.format(
+                                 other_network, updated_mode))
+            setting_to_update.setdefault(other_network, {})
+            setting_to_update[other_network]['bandwidth'] = updated_mode
 
         self.update_ap_settings(setting_to_update)
 
@@ -196,67 +200,64 @@
         self.read_radio_on_off()
         # Get radio configuration. Note that if both radios are off, the below
         # code will result in an error
-        with BlockingBrowser(self.ap_settings["headless_browser"],
+        with BlockingBrowser(self.ap_settings['headless_browser'],
                              900) as browser:
             browser.visit_persistent(self.config_page,
                                      BROWSER_WAIT_MED,
                                      10,
-                                     check_for_element="wireless")
-            wireless_button = browser.find_by_id("wireless").first
+                                     check_for_element='wireless')
+            wireless_button = browser.find_by_id('wireless').first
             wireless_button.click()
             time.sleep(BROWSER_WAIT_MED)
 
-            with browser.get_iframe("formframe") as iframe:
+            with browser.get_iframe('formframe') as iframe:
                 # read if 11ax is enabled first
                 config_item = iframe.find_by_name('enable_ax').first
                 self.ap_settings['enable_ax'] = int(config_item.checked)
                 # read rest of configuration
                 for key, value in self.config_page_fields.items():
-                    if "bandwidth" in key:
+                    if 'bandwidth' in key:
                         config_item = iframe.find_by_name(value).first
-                        self.ap_settings["{}_{}".format(
-                            key[1], key[0])] = self.bw_mode_values[
-                                self.ap_settings['enable_ax']][
-                                    config_item.value]
-                    elif "region" in key:
+                        self.ap_settings[key[0]][key[1]] = self.bw_mode_values[
+                            self.ap_settings['enable_ax']][config_item.value]
+                    elif 'region' in key:
                         config_item = iframe.find_by_name(value).first
-                        self.ap_settings["region"] = self.region_map[
+                        self.ap_settings['region'] = self.region_map[
                             config_item.value]
-                    elif "password" in key:
+                    elif 'password' in key:
                         try:
                             config_item = iframe.find_by_name(value).first
-                            self.ap_settings["{}_{}".format(
-                                key[1], key[0])] = config_item.value
-                            self.ap_settings["{}_{}".format(
-                                "security_type", key[0])] = "WPA2-PSK"
+                            self.ap_settings[key[0]][
+                                key[1]] = config_item.value
+                            self.ap_settings[
+                                key[0]]['security_type'] = 'WPA2-PSK'
                         except:
-                            self.ap_settings["{}_{}".format(
-                                key[1], key[0])] = "defaultpassword"
-                            self.ap_settings["{}_{}".format(
-                                "security_type", key[0])] = "Disable"
-                    elif ("channel" in key) or ("ssid" in key):
+                            self.ap_settings[key[0]][
+                                key[1]] = 'defaultpassword'
+                            self.ap_settings[
+                                key[0]]['security_type'] = 'Disable'
+                    elif ('channel' in key) or ('ssid' in key):
                         config_item = iframe.find_by_name(value).first
-                        self.ap_settings["{}_{}".format(
-                            key[1], key[0])] = config_item.value
+                        self.ap_settings[key[0]][key[1]] = config_item.value
         return self.ap_settings.copy()
 
     def configure_ap(self, **config_flags):
         """Function to configure ap wireless settings."""
         # Turn radios on or off
-        if config_flags["status_toggled"]:
+        if config_flags['status_toggled']:
             self.configure_radio_on_off()
         # Configure radios
-        with BlockingBrowser(self.ap_settings["headless_browser"],
+        with BlockingBrowser(self.ap_settings['headless_browser'],
                              900) as browser:
             browser.visit_persistent(self.config_page,
                                      BROWSER_WAIT_MED,
                                      10,
-                                     check_for_element="wireless")
-            wireless_button = browser.find_by_id("wireless").first
+                                     check_for_element='wireless')
+            wireless_button = browser.find_by_id('wireless').first
             wireless_button.click()
             time.sleep(BROWSER_WAIT_MED)
 
-            with browser.get_iframe("formframe") as iframe:
+            with browser.get_iframe('formframe') as iframe:
                 # Create action chain
                 action = selenium.webdriver.common.action_chains.ActionChains(
                     browser.driver)
@@ -265,60 +266,59 @@
                     iframe.find_by_name('enable_ax').first.checked)
                 if self.ap_settings['enable_ax'] != curr_ax_enabled:
                     ax_checkbox = browser.driver.find_element_by_id(
-                        "enable_ax_chec")
+                        'enable_ax_chec')
                     action.move_to_element(ax_checkbox).click().perform()
                 # Update AP region. Must be done before channel setting
                 config_item = iframe.find_by_name(
-                    self.config_page_fields["region"]).first
-                config_item.select_by_text(self.ap_settings["region"])
+                    self.config_page_fields['region']).first
+                config_item.select_by_text(self.ap_settings['region'])
                 # Update wireless settings for each network
                 for key, value in self.config_page_fields.items():
-                    if "ssid" in key:
+                    if 'ssid' in key:
                         config_item = iframe.find_by_name(value).first
-                        config_item.fill(self.ap_settings["{}_{}".format(
-                            key[1], key[0])])
-                    elif "channel" in key:
-                        channel_string = "0" * (int(self.ap_settings[
-                            "{}_{}".format(key[1], key[0])]) < 10) + str(
-                                self.ap_settings["{}_{}".format(
-                                    key[1], key[0])]) + "(DFS)" * (48 < int(
-                                        self.ap_settings["{}_{}".format(
-                                            key[1], key[0])]) < 149)
+                        config_item.fill(self.ap_settings[key[0]][key[1]])
+                    elif 'channel' in key:
+                        channel = self.ap_settings[key[0]][key[1]]
+                        if int(channel) < 10:
+                            channel_string = '0' + str(channel)
+                        elif int(channel) > 48 and int(channel) < 149:
+                            channel_string = str(channel) + 'DFS'
+                        else:
+                            channel_string = str(channel)
                         config_item = iframe.find_by_name(value).first
                         try:
                             config_item.select_by_text(channel_string)
                         except AttributeError:
                             self.log.warning(
-                                "Cannot select channel. Keeping AP default.")
-                    elif "bandwidth" in key:
+                                'Cannot select channel. Keeping AP default.')
+                    elif 'bandwidth' in key:
                         config_item = iframe.find_by_name(value).first
                         try:
                             config_item.select_by_text(
                                 str(self.bw_mode_text[key[0]][self.ap_settings[
-                                    "{}_{}".format(key[1], key[0])]]))
+                                    key[0]][key[1]]]))
                         except AttributeError:
                             self.log.warning(
-                                "Cannot select bandwidth. Keeping AP default.")
+                                'Cannot select bandwidth. Keeping AP default.')
                 # Update passwords for WPA2-PSK protected networks
                 # (Must be done after security type is selected)
                 for key, value in self.config_page_fields.items():
-                    if "security_type" in key:
+                    if 'security_type' in key:
                         security_option = browser.driver.find_element_by_id(
                             self.security_mode_values[key[0]][self.ap_settings[
-                                "{}_{}".format(key[1], key[0])]])
+                                key[0]][key[1]]])
                         action = selenium.webdriver.common.action_chains.ActionChains(
                             browser.driver)
                         action.move_to_element(
                             security_option).click().perform()
-                        if self.ap_settings["{}_{}".format(
-                                key[1], key[0])] == "WPA2-PSK":
+                        if self.ap_settings[key[0]][key[1]] == 'WPA2-PSK':
                             config_item = iframe.find_by_name(
                                 self.config_page_fields[(key[0],
-                                                         "password")]).first
-                            config_item.fill(self.ap_settings["{}_{}".format(
-                                "password", key[0])])
+                                                         'password')]).first
+                            config_item.fill(
+                                self.ap_settings[key[0]]['password'])
 
-                apply_button = iframe.find_by_name("Apply")
+                apply_button = iframe.find_by_name('Apply')
                 apply_button[0].click()
                 time.sleep(BROWSER_WAIT_SHORT)
                 try:
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
new file mode 100644
index 0000000..91c6382
--- /dev/null
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax200.py
@@ -0,0 +1,363 @@
+#!/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 collections
+import selenium
+import time
+from acts_contrib.test_utils.wifi.wifi_retail_ap import WifiRetailAP
+from acts_contrib.test_utils.wifi.wifi_retail_ap import BlockingBrowser
+
+BROWSER_WAIT_SHORT = 1
+BROWSER_WAIT_MED = 3
+BROWSER_WAIT_LONG = 30
+BROWSER_WAIT_EXTRA_LONG = 60
+
+
+class NetgearRAX200AP(WifiRetailAP):
+    """Class that implements Netgear RAX200 AP.
+
+    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()
+        # Read and update AP settings
+        self.read_ap_settings()
+        self.update_ap_settings(ap_settings)
+
+    def init_gui_data(self):
+        self.config_page = (
+            '{protocol}://{username}:{password}@'
+            '{ip_address}:{port}/WLG_wireless_tri_band.htm').format(
+                protocol=self.ap_settings['protocol'],
+                username=self.ap_settings['admin_username'],
+                password=self.ap_settings['admin_password'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
+        self.config_page_nologin = (
+            '{protocol}://{ip_address}:{port}/'
+            'WLG_wireless_tri_band.htm').format(
+                protocol=self.ap_settings['protocol'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
+        self.config_page_advanced = (
+            '{protocol}://{username}:{password}@'
+            '{ip_address}:{port}/WLG_adv_tri_band2.htm').format(
+                protocol=self.ap_settings['protocol'],
+                username=self.ap_settings['admin_username'],
+                password=self.ap_settings['admin_password'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
+        self.capabilities = {
+            'interfaces': ['2G', '5G_1', '5G_2'],
+            'channels': {
+                '2G': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+                '5G_1': [36, 40, 44, 48, 52, 56, 60, 64],
+                '5G_2': [
+                    100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
+                    149, 153, 157, 161, 165
+                ]
+            },
+            'modes': {
+                '2G': ['VHT20', 'VHT40', 'HE20', 'HE40'],
+                '5G_1': [
+                    'VHT20', 'VHT40', 'VHT80', 'VHT160', 'HE20', 'HE40',
+                    'HE80', 'HE160'
+                ],
+                '5G_2': [
+                    'VHT20', 'VHT40', 'VHT80', 'VHT160', 'HE20', 'HE40',
+                    'HE80', 'HE160'
+                ]
+            },
+            'default_mode': 'HE'
+        }
+        for interface in self.capabilities['interfaces']:
+            self.ap_settings[interface] = {}
+
+        self.region_map = {
+            '3': 'Australia',
+            '4': 'Canada',
+            '5': 'Europe',
+            '7': 'Japan',
+            '8': 'Korea',
+            '11': 'North America',
+            '16': 'China',
+            '17': 'India',
+            '21': 'Middle East(Saudi Arabia/United Arab Emirates)',
+            '23': 'Singapore',
+            '25': 'Hong Kong',
+            '26': 'Vietnam'
+        }
+
+        self.bw_mode_text = {
+            '2G': {
+                '11g': 'Up to 54 Mbps',
+                'HE20': 'Up to 600 Mbps',
+                'HE40': 'Up to 1200 Mbps',
+                'VHT20': 'Up to 433 Mbps',
+                'VHT40': 'Up to 1000 Mbps'
+            },
+            '5G_1': {
+                'HE20': 'Up to 600 Mbps',
+                'HE40': 'Up to 1200 Mbps',
+                'HE80': 'Up to 2400 Mbps',
+                'HE160': 'Up to 4800 Mbps',
+                'VHT20': 'Up to 433 Mbps',
+                'VHT40': 'Up to 1000 Mbps',
+                'VHT80': 'Up to 2165 Mbps',
+                'VHT160': 'Up to 4330'
+            },
+            '5G_2': {
+                'HE20': 'Up to 600 Mbps',
+                'HE40': 'Up to 1200 Mbps',
+                'HE80': 'Up to 2400 Mbps',
+                'HE160': 'Up to 4800 Mbps',
+                'VHT20': 'Up to 433 Mbps',
+                'VHT40': 'Up to 1000 Mbps',
+                'VHT80': 'Up to 2165 Mbps',
+                'VHT160': 'Up to 4330'
+            }
+        }
+        self.bw_mode_values = {
+            # first key is a boolean indicating if 11ax is enabled
+            0: {
+                'g and b': '11g',
+                '145Mbps': 'VHT20',
+                '300Mbps': 'VHT40',
+                'HT80': 'VHT80',
+                'HT160': 'VHT160'
+            },
+            1: {
+                'g and b': '11g',
+                '145Mbps': 'HE20',
+                '300Mbps': 'HE40',
+                'HT80': 'HE80',
+                'HT160': 'HE160'
+            }
+        }
+
+        # Config ordering intentional to avoid GUI bugs
+        self.config_page_fields = collections.OrderedDict([
+            ('region', 'WRegion'), ('enable_ax', 'enable_he'),
+            (('2G', 'status'), 'enable_ap'),
+            (('5G_1', 'status'), 'enable_ap_an'),
+            (('5G_2', 'status'), 'enable_ap_an_2'), (('2G', 'ssid'), 'ssid'),
+            (('5G_1', 'ssid'), 'ssid_an'), (('5G_2', 'ssid'), 'ssid_an_2'),
+            (('2G', 'channel'), 'w_channel'),
+            (('5G_1', 'channel'), 'w_channel_an'),
+            (('5G_2', 'channel'), 'w_channel_an_2'),
+            (('2G', 'bandwidth'), 'opmode'),
+            (('5G_1', 'bandwidth'), 'opmode_an'),
+            (('5G_2', 'bandwidth'), 'opmode_an_2'),
+            (('2G', 'power'), 'enable_tpc'),
+            (('5G_1', 'power'), 'enable_tpc_an'),
+            (('5G_2', 'power'), 'enable_tpc_an_2'),
+            (('5G_2', 'security_type'), 'security_type_an_2'),
+            (('5G_1', 'security_type'), 'security_type_an'),
+            (('2G', 'security_type'), 'security_type'),
+            (('2G', 'password'), 'passphrase'),
+            (('5G_1', 'password'), 'passphrase_an'),
+            (('5G_2', 'password'), 'passphrase_an_2')
+        ])
+
+        self.power_mode_values = {
+            '1': '100%',
+            '2': '75%',
+            '3': '50%',
+            '4': '25%'
+        }
+
+    def set_bandwidth(self, network, bandwidth):
+        """Function that sets network bandwidth/mode.
+
+        Args:
+            network: string containing network identifier (2G, 5G_1, 5G_2)
+            bandwidth: string containing mode, e.g. 11g, VHT20, VHT40, VHT80.
+        """
+        if 'bw' in bandwidth:
+            bandwidth = bandwidth.replace('bw',
+                                          self.capabilities['default_mode'])
+        if bandwidth not in self.capabilities['modes'][network]:
+            self.log.error('{} mode is not supported on {} interface.'.format(
+                bandwidth, network))
+        setting_to_update = {network: {'bandwidth': str(bandwidth)}}
+        setting_to_update['enable_ax'] = int('HE' in bandwidth)
+        # Check if other interfaces need to be changed too
+        requested_mode = 'HE' if 'HE' in bandwidth else 'VHT'
+        for other_network in self.capabilities['interfaces']:
+            if other_network == network:
+                continue
+            other_mode = 'HE' if 'HE' in self.ap_settings[other_network][
+                'bandwidth'] else 'VHT'
+            other_bw = ''.join([
+                x for x in self.ap_settings[other_network]['bandwidth']
+                if x.isdigit()
+            ])
+            if other_mode != requested_mode:
+                updated_mode = '{}{}'.format(requested_mode, other_bw)
+                self.log.warning('All networks must be VHT or HE. '
+                                 'Updating {} to {}'.format(
+                                     other_network, updated_mode))
+                setting_to_update.setdefault(other_network, {})
+                setting_to_update[other_network]['bandwidth'] = updated_mode
+
+        self.update_ap_settings(setting_to_update)
+
+    def read_ap_settings(self):
+        """Function to read ap settings."""
+        with BlockingBrowser(self.ap_settings['headless_browser'],
+                             900) as browser:
+            # 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:
+                    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)
+                    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:
+                        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
+                    else:
+                        config_item = browser.find_by_name(value)
+                        self.ap_settings[key[0]][
+                            key[1]] = config_item.first.value
+        return self.ap_settings.copy()
+
+    def configure_ap(self, **config_flags):
+        """Function to configure ap wireless settings."""
+        # Turn radios on or off
+        if config_flags['status_toggled']:
+            self.configure_radio_on_off()
+        # Configure radios
+        with BlockingBrowser(self.ap_settings['headless_browser'],
+                             900) as browser:
+            # Visit URL
+            browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+            browser.visit_persistent(self.config_page_nologin,
+                                     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:
+                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
+                    try:
+                        config_item.select_by_text(self.bw_mode_text[key[0]][
+                            self.ap_settings[key[0]][key[1]]])
+                    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 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
+                    try:
+                        config_item.select(self.ap_settings[key[0]][key[1]])
+                        time.sleep(BROWSER_WAIT_SHORT)
+                    except AttributeError:
+                        self.log.warning(
+                            'Cannot select channel. Keeping AP default.')
+                    try:
+                        alert = browser.get_alert()
+                        alert.accept()
+                    except:
+                        pass
+            time.sleep(BROWSER_WAIT_SHORT)
+            browser.find_by_name('Apply').first.click()
+            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)
+        self.validate_ap_settings()
+
+    def configure_radio_on_off(self):
+        """Helper configuration function to turn radios on/off."""
+        with BlockingBrowser(self.ap_settings['headless_browser'],
+                             900) as browser:
+            # Visit URL
+            browser.visit_persistent(self.config_page, BROWSER_WAIT_MED, 10)
+            browser.visit_persistent(self.config_page_advanced,
+                                     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()
+
+            time.sleep(BROWSER_WAIT_SHORT)
+            browser.find_by_name('Apply').first.click()
+            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_rax80.py b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax80.py
index 9c4ca1d..c6c104b 100644
--- a/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax80.py
+++ b/acts_tests/acts_contrib/test_utils/wifi/wifi_retail_ap/netgear_rax80.py
@@ -2,14 +2,14 @@
 #
 #   Copyright 2020 - The Android Open Source Project
 #
-#   Licensed under the Apache License, Version 2.0 (the "License");
+#   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,
+#   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.
@@ -27,48 +27,55 @@
         super().init_gui_data()
         # Overwrite minor differences from R7000 AP
         self.config_page = (
-            "{protocol}://{username}:{password}@"
-            "{ip_address}:{port}/WLG_wireless_dual_band_r10.htm").format(
-                protocol=self.ap_settings["protocol"],
-                username=self.ap_settings["admin_username"],
-                password=self.ap_settings["admin_password"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
+            '{protocol}://{username}:{password}@'
+            '{ip_address}:{port}/WLG_wireless_dual_band_r10.htm').format(
+                protocol=self.ap_settings['protocol'],
+                username=self.ap_settings['admin_username'],
+                password=self.ap_settings['admin_password'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
         self.config_page_nologin = (
-            "{protocol}://{ip_address}:{port}/"
-            "WLG_wireless_dual_band_r10.htm").format(
-                protocol=self.ap_settings["protocol"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
+            '{protocol}://{ip_address}:{port}/'
+            'WLG_wireless_dual_band_r10.htm').format(
+                protocol=self.ap_settings['protocol'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
         self.config_page_advanced = (
-            "{protocol}://{username}:{password}@"
-            "{ip_address}:{port}/WLG_adv_dual_band2.htm").format(
-                protocol=self.ap_settings["protocol"],
-                username=self.ap_settings["admin_username"],
-                password=self.ap_settings["admin_password"],
-                ip_address=self.ap_settings["ip_address"],
-                port=self.ap_settings["port"])
-        self.networks = ["2G", "5G_1", "5G_2"]
-        self.channel_band_map = {
-            "2G": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
-            "5G_1": [
-                36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120,
-                124, 128, 132, 136, 140, 149, 153, 157, 161, 165
-            ]
+            '{protocol}://{username}:{password}@'
+            '{ip_address}:{port}/WLG_adv_dual_band2.htm').format(
+                protocol=self.ap_settings['protocol'],
+                username=self.ap_settings['admin_username'],
+                password=self.ap_settings['admin_password'],
+                ip_address=self.ap_settings['ip_address'],
+                port=self.ap_settings['port'])
+        self.capabilities = {
+            'interfaces': ['2G', '5G_1', '5G_2'],
+            'channels': {
+                '2G': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
+                '5G_1': [36, 40, 44, 48],
+                '5G_2': [149, 153, 157, 161, 165]
+            },
+            'modes': {
+                '2G': ['VHT20', 'VHT40'],
+                '5G_1': ['VHT20', 'VHT40', 'VHT80'],
+                '5G_2': ['VHT20', 'VHT40', 'VHT80']
+            },
+            'default_mode': 'VHT'
         }
+        for interface in self.capabilities['interfaces']:
+            self.ap_settings[interface] = {}
 
         self.bw_mode_values = {
-            "g and b": "11g",
-            "145Mbps": "VHT20",
-            "300Mbps": "VHT40",
-            "HT80": "VHT80",
-            "HT160": "VHT160"
+            'g and b': '11g',
+            '145Mbps': 'VHT20',
+            '300Mbps': 'VHT40',
+            'HT80': 'VHT80',
+            'HT160': 'VHT160'
         }
         self.bw_mode_text = {
-            "11g": "Up to 54 Mbps",
-            "VHT20": "Up to 600 Mbps",
-            "VHT40": "Up to 1200 Mbps",
-            "VHT80": "Up to 2400 Mbps",
-            "VHT160": "Up to 4800 Mbps"
+            '11g': 'Up to 54 Mbps',
+            'VHT20': 'Up to 600 Mbps',
+            'VHT40': 'Up to 1200 Mbps',
+            'VHT80': 'Up to 2400 Mbps',
+            'VHT160': 'Up to 4800 Mbps'
         }
-        self.default_mode = 'HE'
diff --git a/acts_tests/tests/google/wifi/WifiPingTest.py b/acts_tests/tests/google/wifi/WifiPingTest.py
index 0fb3823..4d25f26 100644
--- a/acts_tests/tests/google/wifi/WifiPingTest.py
+++ b/acts_tests/tests/google/wifi/WifiPingTest.py
@@ -91,7 +91,7 @@
         if self.testclass_params.get('airplane_mode', 1):
             self.log.info('Turning on airplane mode.')
             asserts.assert_true(utils.force_airplane_mode(self.dut, True),
-                                "Can not turn on airplane mode.")
+                                'Can not turn on airplane mode.')
         wutils.wifi_toggle_state(self.dut, True)
 
         # Configure test retries
@@ -193,7 +193,7 @@
                         'LLStats at Range: {}'.format(
                             result['range'], result['llstats_at_range']))
         if result['peak_throughput_pct'] < 95:
-            asserts.fail("(RESULT NOT RELIABLE) {}".format(test_message))
+            asserts.fail('(RESULT NOT RELIABLE) {}'.format(test_message))
 
         # If pass, set Blackbox metric
         if self.publish_testcase_metrics:
@@ -299,7 +299,7 @@
             self.sniffer.start_capture(
                 testcase_params['test_network'],
                 chan=int(testcase_params['channel']),
-                bw=int(testcase_params['mode'][3:]),
+                bw=testcase_params['bandwidth'],
                 duration=testcase_params['ping_duration'] *
                 len(testcase_params['atten_range']) + self.TEST_TIMEOUT)
         # Run ping and sweep attenuation as needed
@@ -409,7 +409,7 @@
             self.atten_dut_chain_map[testcase_params[
                 'channel']] = wputils.get_current_atten_dut_chain_map(
                     self.attenuators, self.dut, self.ping_server)
-        self.log.info("Current Attenuator-DUT Chain Map: {}".format(
+        self.log.info('Current Attenuator-DUT Chain Map: {}'.format(
             self.atten_dut_chain_map[testcase_params['channel']]))
         for idx, atten in enumerate(self.attenuators):
             if self.atten_dut_chain_map[testcase_params['channel']][
@@ -511,18 +511,22 @@
 
     def generate_test_cases(self, ap_power, channels, modes, chain_mask,
                             test_types):
+        """Function that auto-generates test cases for a test class."""
         test_cases = []
         allowed_configs = {
-            'VHT20': [
-                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
-                157, 161
+            20: [
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 64, 100,
+                116, 132, 140, 149, 153, 157, 161
             ],
-            'VHT40': [36, 44, 149, 157],
-            'VHT80': [36, 149]
+            40: [36, 44, 100, 149, 157],
+            80: [36, 100, 149],
+            160: [36]
         }
+
         for channel, mode, chain, test_type in itertools.product(
                 channels, modes, chain_mask, test_types):
-            if channel not in allowed_configs[mode]:
+            bandwidth = int(''.join([x for x in mode if x.isdigit()]))
+            if channel not in allowed_configs[bandwidth]:
                 continue
             testcase_name = '{}_ch{}_{}_ch{}'.format(test_type, channel, mode,
                                                      chain)
@@ -530,6 +534,7 @@
                                                       ap_power=ap_power,
                                                       channel=channel,
                                                       mode=mode,
+                                                      bandwidth=bandwidth,
                                                       chain_mask=chain)
             setattr(self, testcase_name,
                     partial(self._test_ping, testcase_params))
@@ -543,7 +548,7 @@
         self.tests = self.generate_test_cases(
             ap_power='standard',
             channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
-            modes=['VHT20', 'VHT40', 'VHT80'],
+            modes=['bw20', 'bw40', 'bw80'],
             test_types=[
                 'test_ping_range', 'test_fast_ping_rtt', 'test_slow_ping_rtt'
             ],
@@ -557,7 +562,7 @@
             ap_power='standard',
             chain_mask=['0', '1', '2x2'],
             channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
-            modes=['VHT20', 'VHT40', 'VHT80'],
+            modes=['bw20', 'bw40', 'bw80'],
             test_types=['test_ping_range'])
 
 
@@ -568,7 +573,7 @@
             ap_power='low_power',
             chain_mask=['0', '1', '2x2'],
             channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
-            modes=['VHT20', 'VHT40', 'VHT80'],
+            modes=['bw20', 'bw40', 'bw80'],
             test_types=['test_ping_range'])
 
 
@@ -710,16 +715,18 @@
                             positions):
         test_cases = []
         allowed_configs = {
-            'VHT20': [
-                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
-                157, 161
+            20: [
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 64, 100,
+                116, 132, 140, 149, 153, 157, 161
             ],
-            'VHT40': [36, 44, 149, 157],
-            'VHT80': [36, 149]
+            40: [36, 44, 100, 149, 157],
+            80: [36, 100, 149],
+            160: [36]
         }
         for channel, mode, position in itertools.product(
                 channels, modes, positions):
-            if channel not in allowed_configs[mode]:
+            bandwidth = int(''.join([x for x in mode if x.isdigit()]))
+            if channel not in allowed_configs[bandwidth]:
                 continue
             testcase_name = 'test_ping_range_ch{}_{}_pos{}'.format(
                 channel, mode, position)
@@ -728,6 +735,7 @@
                 ap_power=ap_power,
                 channel=channel,
                 mode=mode,
+                bandwidth=bandwidth,
                 chain_mask='2x2',
                 chamber_mode=chamber_mode,
                 total_positions=len(positions),
@@ -743,7 +751,7 @@
         WifiOtaPingTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(ap_power='standard',
                                               channels=[6, 36, 149],
-                                              modes=['VHT20'],
+                                              modes=['bw20'],
                                               chamber_mode='orientation',
                                               positions=list(range(0, 360,
                                                                    10)))
@@ -755,7 +763,7 @@
         self.tests = self.generate_test_cases(
             ap_power='standard',
             channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
-            modes=['VHT20'],
+            modes=['bw20'],
             chamber_mode='orientation',
             positions=list(range(0, 360, 45)))
 
@@ -765,7 +773,7 @@
         WifiOtaPingTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(ap_power='standard',
                                               channels=[6, 36, 149],
-                                              modes=['VHT20'],
+                                              modes=['bw20'],
                                               chamber_mode='stepped stirrers',
                                               positions=list(range(100)))
 
@@ -775,7 +783,7 @@
         WifiOtaPingTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(ap_power='low_power',
                                               channels=[6, 36, 149],
-                                              modes=['VHT20'],
+                                              modes=['bw20'],
                                               chamber_mode='orientation',
                                               positions=list(range(0, 360,
                                                                    10)))
@@ -787,7 +795,7 @@
         self.tests = self.generate_test_cases(
             ap_power='low_power',
             channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
-            modes=['VHT20'],
+            modes=['bw20'],
             chamber_mode='orientation',
             positions=list(range(0, 360, 45)))
 
@@ -797,6 +805,6 @@
         WifiOtaPingTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(ap_power='low_power',
                                               channels=[6, 36, 149],
-                                              modes=['VHT20'],
+                                              modes=['bw20'],
                                               chamber_mode='stepped stirrers',
                                               positions=list(range(100)))
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/WifiRssiTest.py b/acts_tests/tests/google/wifi/WifiRssiTest.py
index 3055985..1c0c6df 100644
--- a/acts_tests/tests/google/wifi/WifiRssiTest.py
+++ b/acts_tests/tests/google/wifi/WifiRssiTest.py
@@ -84,7 +84,7 @@
         if self.testclass_params.get('airplane_mode', 1):
             self.log.info('Turning on airplane mode.')
             asserts.assert_true(utils.force_airplane_mode(self.dut, True),
-                                "Can not turn on airplane mode.")
+                                'Can not turn on airplane mode.')
         wutils.wifi_toggle_state(self.dut, True)
 
     def teardown_test(self):
@@ -785,17 +785,19 @@
         """Function that auto-generates test cases for a test class."""
         test_cases = []
         allowed_configs = {
-            'VHT20': [
-                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
-                157, 161
+            20: [
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 64, 100,
+                116, 132, 140, 149, 153, 157, 161
             ],
-            'VHT40': [36, 44, 149, 157],
-            'VHT80': [36, 149]
+            40: [36, 44, 100, 149, 157],
+            80: [36, 100, 149],
+            160: [36]
         }
 
         for channel, mode, traffic_mode, test_type in itertools.product(
                 channels, modes, traffic_modes, test_types):
-            if channel not in allowed_configs[mode]:
+            bandwidth = int(''.join([x for x in mode if x.isdigit()]))
+            if channel not in allowed_configs[bandwidth]:
                 continue
             test_name = test_type + '_ch{}_{}_{}'.format(
                 channel, mode, traffic_mode)
@@ -817,7 +819,7 @@
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
             ['test_rssi_stability', 'test_rssi_vs_atten'], [1, 2, 6, 10, 11],
-            ['VHT20'], ['ActiveTraffic'])
+            ['bw20'], ['ActiveTraffic'])
 
 
 class WifiRssi_5GHz_ActiveTraffic_Test(WifiRssiTest):
@@ -825,7 +827,7 @@
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
             ['test_rssi_stability', 'test_rssi_vs_atten'],
-            [36, 40, 44, 48, 149, 153, 157, 161], ['VHT20', 'VHT40', 'VHT80'],
+            [36, 40, 44, 48, 149, 153, 157, 161], ['bw20', 'bw40', 'bw80'],
             ['ActiveTraffic'])
 
 
@@ -835,7 +837,7 @@
         self.tests = self.generate_test_cases(
             ['test_rssi_stability', 'test_rssi_vs_atten'],
             [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
-            ['VHT20', 'VHT40', 'VHT80'], ['ActiveTraffic'])
+            ['bw20', 'bw40', 'bw80'], ['ActiveTraffic'])
 
 
 class WifiRssi_SampleChannels_NoTraffic_Test(WifiRssiTest):
@@ -843,7 +845,7 @@
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
             ['test_rssi_stability', 'test_rssi_vs_atten'], [6, 36, 149],
-            ['VHT20', 'VHT40', 'VHT80'], ['NoTraffic'])
+            ['bw20', 'bw40', 'bw80'], ['NoTraffic'])
 
 
 class WifiRssiTrackingTest(WifiRssiTest):
@@ -851,7 +853,7 @@
         super().__init__(controllers)
         self.tests = self.generate_test_cases(['test_rssi_tracking'],
                                               [6, 36, 149],
-                                              ['VHT20', 'VHT40', 'VHT80'],
+                                              ['bw20', 'bw40', 'bw80'],
                                               ['ActiveTraffic', 'NoTraffic'])
 
 
@@ -964,10 +966,10 @@
         Args:
             testcase_params: dict containing test-specific parameters
         """
-        if "rssi_over_orientation" in self.test_name:
+        if 'rssi_over_orientation' in self.test_name:
             rssi_test_duration = self.testclass_params[
                 'rssi_over_orientation_duration']
-        elif "rssi_variation" in self.test_name:
+        elif 'rssi_variation' in self.test_name:
             rssi_test_duration = self.testclass_params[
                 'rssi_variation_duration']
 
@@ -1015,19 +1017,21 @@
                             chamber_modes, orientations):
         test_cases = []
         allowed_configs = {
-            'VHT20': [
-                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
-                157, 161
+            20: [
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 64, 100,
+                116, 132, 140, 149, 153, 157, 161
             ],
-            'VHT40': [36, 44, 149, 157],
-            'VHT80': [36, 149]
+            40: [36, 44, 100, 149, 157],
+            80: [36, 100, 149],
+            160: [36]
         }
 
         for (channel, mode, traffic, chamber_mode, orientation,
              test_type) in itertools.product(channels, modes, traffic_modes,
                                              chamber_modes, orientations,
                                              test_types):
-            if channel not in allowed_configs[mode]:
+            bandwidth = int(''.join([x for x in mode if x.isdigit()]))
+            if channel not in allowed_configs[bandwidth]:
                 continue
             test_name = test_type + '_ch{}_{}_{}_{}deg'.format(
                 channel, mode, traffic, orientation)
@@ -1049,7 +1053,7 @@
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(['test_rssi_vs_atten'],
-                                              [6, 36, 149], ['VHT20'],
+                                              [6, 36, 149], ['bw20'],
                                               ['ActiveTraffic'],
                                               ['orientation'],
                                               list(range(0, 360, 45)))
@@ -1059,7 +1063,7 @@
     def __init__(self, controllers):
         WifiRssiTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(['test_rssi_variation'],
-                                              [6, 36, 149], ['VHT20'],
+                                              [6, 36, 149], ['bw20'],
                                               ['ActiveTraffic'],
                                               ['StirrersOn'], [0])
 
@@ -1068,7 +1072,7 @@
     def __init__(self, controllers):
         WifiRssiTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(['test_rssi_over_orientation'],
-                                              [6, 36, 149], ['VHT20'],
+                                              [6, 36, 149], ['bw20'],
                                               ['ActiveTraffic'],
                                               ['orientation'],
                                               list(range(0, 360, 10)))
diff --git a/acts_tests/tests/google/wifi/WifiRvrTest.py b/acts_tests/tests/google/wifi/WifiRvrTest.py
index befcdad..2a2dcbe 100644
--- a/acts_tests/tests/google/wifi/WifiRvrTest.py
+++ b/acts_tests/tests/google/wifi/WifiRvrTest.py
@@ -99,7 +99,7 @@
             for dev in self.android_devices:
                 self.log.info('Turning on airplane mode.')
                 asserts.assert_true(utils.force_airplane_mode(dev, True),
-                                    "Can not turn on airplane mode.")
+                                    'Can not turn on airplane mode.')
         wutils.wifi_toggle_state(dev, True)
 
     def teardown_test(self):
@@ -304,9 +304,11 @@
             self.testcase_metric_logger.add_metric(
                 'peak_tput', rvr_result['metrics']['peak_tput'])
 
+        test_mode = rvr_result['ap_settings'][rvr_result['testcase_params']
+                                              ['band']]['bandwidth']
         tput_below_limit = [
-            tput < self.testclass_params['tput_metric_targets'][
-                rvr_result['testcase_params']['mode']]['high']
+            tput <
+            self.testclass_params['tput_metric_targets'][test_mode]['high']
             for tput in rvr_result['throughput_receive']
         ]
         rvr_result['metrics']['high_tput_range'] = -1
@@ -324,8 +326,8 @@
                 'high_tput_range', rvr_result['metrics']['high_tput_range'])
 
         tput_below_limit = [
-            tput < self.testclass_params['tput_metric_targets'][
-                rvr_result['testcase_params']['mode']]['low']
+            tput <
+            self.testclass_params['tput_metric_targets'][test_mode]['low']
             for tput in rvr_result['throughput_receive']
         ]
         for idx in range(len(tput_below_limit)):
@@ -374,7 +376,7 @@
                 self.sniffer.start_capture(
                     network=testcase_params['test_network'],
                     chan=int(testcase_params['channel']),
-                    bw=int(testcase_params['mode'][3:]),
+                    bw=testcase_params['bandwidth'],
                     duration=self.testclass_params['iperf_duration'] / 5)
             # Start iperf session
             if self.testclass_params.get('monitor_rssi', 1):
@@ -470,9 +472,7 @@
         Args:
             testcase_params: dict containing AP and other test params
         """
-        band = self.access_point.band_lookup_by_channel(
-            testcase_params['channel'])
-        if '2G' in band:
+        if '2G' in testcase_params['band']:
             frequency = wutils.WifiEnums.channel_2G_to_freq[
                 testcase_params['channel']]
         else:
@@ -482,8 +482,10 @@
             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(testcase_params['band'],
+                                      testcase_params['channel'])
+        self.access_point.set_bandwidth(testcase_params['band'],
+                                        testcase_params['mode'])
         self.log.info('Access Point Configuration: {}'.format(
             self.access_point.ap_settings))
 
@@ -512,7 +514,7 @@
                 self.sniffer.start_capture(
                     network={'SSID': testcase_params['test_network']['SSID']},
                     chan=testcase_params['channel'],
-                    bw=testcase_params['mode'][3:],
+                    bw=testcase_params['bandwidth'],
                     duration=180)
             try:
                 wutils.wifi_connect(self.sta_dut,
@@ -571,6 +573,7 @@
         ]
         band = self.access_point.band_lookup_by_channel(
             testcase_params['channel'])
+        testcase_params['band'] = band
         testcase_params['test_network'] = self.main_network[band]
         if testcase_params['traffic_type'] == 'TCP':
             testcase_params['iperf_socket_size'] = self.testclass_params.get(
@@ -591,7 +594,9 @@
                 reverse_direction=1,
                 traffic_type=testcase_params['traffic_type'],
                 socket_size=testcase_params['iperf_socket_size'],
-                num_processes=testcase_params['iperf_processes'])
+                num_processes=testcase_params['iperf_processes'],
+                udp_throughput=self.testclass_params['UDP_rates'][
+                    testcase_params['mode']])
             testcase_params['use_client_output'] = True
         else:
             testcase_params['iperf_args'] = wputils.get_iperf_arg_string(
@@ -599,7 +604,9 @@
                 reverse_direction=0,
                 traffic_type=testcase_params['traffic_type'],
                 socket_size=testcase_params['iperf_socket_size'],
-                num_processes=testcase_params['iperf_processes'])
+                num_processes=testcase_params['iperf_processes'],
+                udp_throughput=self.testclass_params['UDP_rates'][
+                    testcase_params['mode']])
             testcase_params['use_client_output'] = False
         return testcase_params
 
@@ -645,6 +652,7 @@
             test_params = collections.OrderedDict(
                 channel=channel,
                 mode=mode,
+                bandwidth=bandwidth,
                 traffic_type=traffic_type,
                 traffic_direction=traffic_direction)
             setattr(self, test_name, partial(self._test_rvr, test_params))
@@ -652,62 +660,12 @@
         return test_cases
 
 
-# Classes defining test suites
-class WifiRvr_2GHz_Test(WifiRvrTest):
-    def __init__(self, controllers):
-        super().__init__(controllers)
-        self.tests = self.generate_test_cases(channels=[1, 6, 11],
-                                              modes=['VHT20'],
-                                              traffic_types=['TCP'],
-                                              traffic_directions=['DL', 'UL'])
-
-
-class WifiRvr_UNII1_Test(WifiRvrTest):
-    def __init__(self, controllers):
-        super().__init__(controllers)
-        self.tests = self.generate_test_cases(
-            channels=[36, 40, 44, 48],
-            modes=['VHT20', 'VHT40', 'VHT80'],
-            traffic_types=['TCP'],
-            traffic_directions=['DL', 'UL'])
-
-
-class WifiRvr_UNII3_Test(WifiRvrTest):
-    def __init__(self, controllers):
-        super().__init__(controllers)
-        self.tests = self.generate_test_cases(
-            channels=[149, 153, 157, 161],
-            modes=['VHT20', 'VHT40', 'VHT80'],
-            traffic_types=['TCP'],
-            traffic_directions=['DL', 'UL'])
-
-
-class WifiRvr_SampleDFS_Test(WifiRvrTest):
-    def __init__(self, controllers):
-        super().__init__(controllers)
-        self.tests = self.generate_test_cases(
-            channels=[64, 100, 116, 132, 140],
-            modes=['VHT20', 'VHT40', 'VHT80'],
-            traffic_types=['TCP'],
-            traffic_directions=['DL', 'UL'])
-
-
-class WifiRvr_SampleUDP_Test(WifiRvrTest):
-    def __init__(self, controllers):
-        super().__init__(controllers)
-        self.tests = self.generate_test_cases(
-            channels=[6, 36, 149],
-            modes=['VHT20', 'VHT40', 'VHT80'],
-            traffic_types=['UDP'],
-            traffic_directions=['DL', 'UL'])
-
-
 class WifiRvr_TCP_Test(WifiRvrTest):
     def __init__(self, controllers):
         super().__init__(controllers)
         self.tests = self.generate_test_cases(
             channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
-            modes=['VHT20', 'VHT40', 'VHT80'],
+            modes=['bw20', 'bw40', 'bw80', 'bw160'],
             traffic_types=['TCP'],
             traffic_directions=['DL', 'UL'])
 
@@ -732,6 +690,46 @@
             traffic_directions=['DL', 'UL'])
 
 
+class WifiRvr_SampleUDP_Test(WifiRvrTest):
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.tests = self.generate_test_cases(
+            channels=[6, 36, 149],
+            modes=['bw20', 'bw40', 'bw80', 'bw160'],
+            traffic_types=['UDP'],
+            traffic_directions=['DL', 'UL'])
+
+
+class WifiRvr_VHT_SampleUDP_Test(WifiRvrTest):
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.tests = self.generate_test_cases(
+            channels=[6, 36, 149],
+            modes=['VHT20', 'VHT40', 'VHT80', 'VHT160'],
+            traffic_types=['UDP'],
+            traffic_directions=['DL', 'UL'])
+
+
+class WifiRvr_HE_SampleUDP_Test(WifiRvrTest):
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.tests = self.generate_test_cases(
+            channels=[6, 36, 149],
+            modes=['HE20', 'HE40', 'HE80', 'HE160'],
+            traffic_types=['UDP'],
+            traffic_directions=['DL', 'UL'])
+
+
+class WifiRvr_SampleDFS_Test(WifiRvrTest):
+    def __init__(self, controllers):
+        super().__init__(controllers)
+        self.tests = self.generate_test_cases(
+            channels=[64, 100, 116, 132, 140],
+            modes=['bw20', 'bw40', 'bw80'],
+            traffic_types=['TCP'],
+            traffic_directions=['DL', 'UL'])
+
+
 # Over-the air version of RVR tests
 class WifiOtaRvrTest(WifiRvrTest):
     """Class to test over-the-air RvR
@@ -816,7 +814,7 @@
             self.testclass_metric_logger.add_metric(
                 '{}.high_tput_hit_freq'.format(metric_tag), high_tput_hit_freq)
             for metric_key, metric_value in test_data['metrics'].items():
-                metric_key = "{}.avg_{}".format(metric_tag, metric_key)
+                metric_key = '{}.avg_{}'.format(metric_tag, metric_key)
                 metric_value = numpy.mean(metric_value)
                 self.testclass_metric_logger.add_metric(
                     metric_key, metric_value)
@@ -848,21 +846,24 @@
                             directions):
         test_cases = []
         allowed_configs = {
-            'VHT20': [
-                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
-                157, 161
+            20: [
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 64, 100,
+                116, 132, 140, 149, 153, 157, 161
             ],
-            'VHT40': [36, 44, 149, 157],
-            'VHT80': [36, 149]
+            40: [36, 44, 100, 149, 157],
+            80: [36, 100, 149],
+            160: [36]
         }
         for channel, mode, angle, traffic_type, direction in itertools.product(
                 channels, modes, angles, traffic_types, directions):
-            if channel not in allowed_configs[mode]:
+            bandwidth = int(''.join([x for x in mode if x.isdigit()]))
+            if channel not in allowed_configs[bandwidth]:
                 continue
             testcase_name = 'test_rvr_{}_{}_ch{}_{}_{}deg'.format(
                 traffic_type, direction, channel, mode, angle)
             test_params = collections.OrderedDict(channel=channel,
                                                   mode=mode,
+                                                  bandwidth=bandwidth,
                                                   traffic_type=traffic_type,
                                                   traffic_direction=direction,
                                                   orientation=angle)
@@ -876,18 +877,17 @@
         WifiOtaRvrTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(
             [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
-            ['VHT20', 'VHT40', 'VHT80'], list(range(0, 360,
-                                                    45)), ['TCP'], ['DL'])
+            ['bw20', 'bw40', 'bw80'], list(range(0, 360, 45)), ['TCP'], ['DL'])
 
 
 class WifiOtaRvr_SampleChannel_Test(WifiOtaRvrTest):
     def __init__(self, controllers):
         WifiOtaRvrTest.__init__(self, controllers)
-        self.tests = self.generate_test_cases([6], ['VHT20'],
+        self.tests = self.generate_test_cases([6], ['bw20'],
                                               list(range(0, 360, 45)), ['TCP'],
                                               ['DL'])
         self.tests.extend(
-            self.generate_test_cases([36, 149], ['VHT80'],
+            self.generate_test_cases([36, 149], ['bw80'],
                                      list(range(0, 360, 45)), ['TCP'], ['DL']))
 
 
@@ -895,5 +895,5 @@
     def __init__(self, controllers):
         WifiOtaRvrTest.__init__(self, controllers)
         self.tests = self.generate_test_cases(
-            [6, 36, 40, 44, 48, 149, 153, 157, 161],
-            ['VHT20', 'VHT40', 'VHT80'], [0], ['TCP'], ['DL', 'UL'])
+            [6, 36, 40, 44, 48, 149, 153, 157, 161], ['bw20', 'bw40', 'bw80'],
+            [0], ['TCP'], ['DL', 'UL'])
diff --git a/acts_tests/tests/google/wifi/WifiSensitivityTest.py b/acts_tests/tests/google/wifi/WifiSensitivityTest.py
index 3172aaa..b876ae9 100644
--- a/acts_tests/tests/google/wifi/WifiSensitivityTest.py
+++ b/acts_tests/tests/google/wifi/WifiSensitivityTest.py
@@ -162,7 +162,7 @@
         if self.testclass_params.get('airplane_mode', 1):
             self.log.info('Turning on airplane mode.')
             asserts.assert_true(utils.force_airplane_mode(self.dut, True),
-                                "Can not turn on airplane mode.")
+                                'Can not turn on airplane mode.')
         wutils.wifi_toggle_state(self.dut, True)
 
         # Configure test retries
@@ -245,7 +245,7 @@
             metric_tag = 'ch{}_{}_nss{}_chain{}'.format(
                 metric_tag_dict['channel'], metric_tag_dict['mode'],
                 metric_tag_dict['num_streams'], metric_tag_dict['chain_mask'])
-            metric_key = "{}.avg_sensitivity".format(metric_tag)
+            metric_key = '{}.avg_sensitivity'.format(metric_tag)
             metric_value = numpy.nanmean(metric_data)
             self.testclass_metric_logger.add_metric(metric_key, metric_value)
 
@@ -410,7 +410,7 @@
             self.atten_dut_chain_map[testcase_params[
                 'channel']] = wputils.get_current_atten_dut_chain_map(
                     self.attenuators, self.dut, self.ping_server)
-        self.log.info("Current Attenuator-DUT Chain Map: {}".format(
+        self.log.info('Current Attenuator-DUT Chain Map: {}'.format(
             self.atten_dut_chain_map[testcase_params['channel']]))
         for idx, atten in enumerate(self.attenuators):
             if self.atten_dut_chain_map[testcase_params['channel']][
@@ -510,7 +510,7 @@
         return testcase_params
 
     def _test_sensitivity(self, testcase_params):
-        """ Function that gets called for each test case
+        """Function that gets called for each test case
 
         The function gets called in each rvr test case. The function customizes
         the rvr test based on the test name of the test that called it
@@ -545,6 +545,7 @@
                 if mode in self.VALID_TEST_CONFIGS[channel]
             ]
             for mode in requested_modes:
+                bandwidth = int(''.join([x for x in mode if x.isdigit()]))
                 if 'VHT' in mode:
                     rates = self.VALID_RATES[mode]
                 elif 'HT' in mode:
@@ -559,6 +560,7 @@
                     testcase_params = collections.OrderedDict(
                         channel=channel,
                         mode=mode,
+                        bandwidth=bandwidth,
                         rate=rate.mcs,
                         num_streams=rate.streams,
                         short_gi=1,
@@ -765,7 +767,7 @@
                 metric_value = numpy.nanmean(line_results['sensitivity'])
                 self.testclass_metric_logger.add_metric(
                     metric_name, metric_value)
-                self.log.info(("Average Sensitivity for {}: {:.1f}").format(
+                self.log.info(('Average Sensitivity for {}: {:.1f}').format(
                     metric_tag, metric_value))
             current_context = (
                 context.get_current_context().get_full_output_path())
@@ -825,6 +827,7 @@
                 if mode in self.VALID_TEST_CONFIGS[channel]
             ]
             for chain, mode in itertools.product(chain_mask, requested_modes):
+                bandwidth = int(''.join([x for x in mode if x.isdigit()]))
                 if 'VHT' in mode:
                     valid_rates = self.VALID_RATES[mode]
                 elif 'HT' in mode:
@@ -839,6 +842,7 @@
                     testcase_params = collections.OrderedDict(
                         channel=channel,
                         mode=mode,
+                        bandwidth=bandwidth,
                         rate=rate.mcs,
                         num_streams=rate.streams,
                         short_gi=1,
diff --git a/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py b/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
index 44b4686..59c8575 100644
--- a/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
+++ b/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
@@ -58,7 +58,7 @@
         self.publish_testcase_metrics = True
         # Generate test cases
         self.tests = self.generate_test_cases([6, 36, 149],
-                                              ['VHT20', 'VHT40', 'VHT80'],
+                                              ['bw20', 'bw40', 'bw80'],
                                               ['TCP', 'UDP'], ['DL', 'UL'],
                                               ['high', 'low'])
 
@@ -66,13 +66,15 @@
                             traffic_directions, signal_levels):
         """Function that auto-generates test cases for a test class."""
         allowed_configs = {
-            'VHT20': [
-                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
-                157, 161
+            20: [
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 64, 100,
+                116, 132, 140, 149, 153, 157, 161
             ],
-            'VHT40': [36, 44, 149, 157],
-            'VHT80': [36, 149]
+            40: [36, 44, 100, 149, 157],
+            80: [36, 100, 149],
+            160: [36]
         }
+
         test_cases = []
         for channel, mode, signal_level, traffic_type, traffic_direction in itertools.product(
                 channels,
@@ -81,11 +83,13 @@
                 traffic_types,
                 traffic_directions,
         ):
-            if channel not in allowed_configs[mode]:
+            bandwidth = int(''.join([x for x in mode if x.isdigit()]))
+            if channel not in allowed_configs[bandwidth]:
                 continue
             testcase_params = collections.OrderedDict(
                 channel=channel,
                 mode=mode,
+                bandwidth=bandwidth,
                 traffic_type=traffic_type,
                 traffic_direction=traffic_direction,
                 signal_level=signal_level)
@@ -123,7 +127,7 @@
         if self.testclass_params.get('airplane_mode', 1):
             self.log.info('Turning on airplane mode.')
             asserts.assert_true(utils.force_airplane_mode(self.dut, True),
-                                "Can not turn on airplane mode.")
+                                'Can not turn on airplane mode.')
         wutils.wifi_toggle_state(self.dut, True)
 
     def teardown_test(self):
@@ -565,18 +569,21 @@
                             traffic_directions, signal_levels, chamber_mode,
                             positions):
         allowed_configs = {
-            'VHT20': [
-                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
-                157, 161
+            20: [
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 64, 100,
+                116, 132, 140, 149, 153, 157, 161
             ],
-            'VHT40': [36, 44, 149, 157],
-            'VHT80': [36, 149]
+            40: [36, 44, 100, 149, 157],
+            80: [36, 100, 149],
+            160: [36]
         }
+
         test_cases = []
         for channel, mode, position, traffic_type, signal_level, traffic_direction in itertools.product(
                 channels, modes, positions, traffic_types, signal_levels,
                 traffic_directions):
-            if channel not in allowed_configs[mode]:
+            bandwidth = int(''.join([x for x in mode if x.isdigit()]))
+            if channel not in allowed_configs[bandwidth]:
                 continue
             testcase_params = collections.OrderedDict(
                 channel=channel,
@@ -601,7 +608,7 @@
                                                 ):
     def __init__(self, controllers):
         WifiOtaThroughputStabilityTest.__init__(self, controllers)
-        self.tests = self.generate_test_cases([6, 36, 149], ['VHT20', 'VHT80'],
+        self.tests = self.generate_test_cases([6, 36, 149], ['bw20', 'bw80'],
                                               ['TCP'], ['DL', 'UL'],
                                               ['high', 'low'], 'orientation',
                                               list(range(0, 360, 10)))
@@ -610,7 +617,7 @@
 class WifiOtaThroughputStability_45Degree_Test(WifiOtaThroughputStabilityTest):
     def __init__(self, controllers):
         WifiOtaThroughputStabilityTest.__init__(self, controllers)
-        self.tests = self.generate_test_cases([6, 36, 149], ['VHT20', 'VHT80'],
+        self.tests = self.generate_test_cases([6, 36, 149], ['bw20', 'bw80'],
                                               ['TCP'], ['DL', 'UL'],
                                               ['high', 'low'], 'orientation',
                                               list(range(0, 360, 45)))
@@ -620,7 +627,7 @@
         WifiOtaThroughputStabilityTest):
     def __init__(self, controllers):
         WifiOtaThroughputStabilityTest.__init__(self, controllers)
-        self.tests = self.generate_test_cases([6, 36, 149], ['VHT20', 'VHT80'],
+        self.tests = self.generate_test_cases([6, 36, 149], ['bw20', 'bw80'],
                                               ['TCP'], ['DL', 'UL'],
                                               ['high', 'low'],
                                               'stepped stirrers',