resolve merge conflicts of 9bce15d9087a80c2bf565717e9b61c1a2fcd02e3 to stage-aosp-master

BUG: None
Test: I solemnly swear I tested this conflict resolution.
Change-Id: I41cfea9d74d5f044866ade338d187b469acb4c4d
diff --git a/Android.mk b/Android.mk
index 4040a1c..11b923b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,11 +23,11 @@
 # general Android Conntectivity Test Suite
 ACTS_DISTRO := $(HOST_OUT)/acts-dist/acts.zip
 
-$(ACTS_DISTRO): $(sort $(shell find $(LOCAL_PATH)/acts/framework))
+$(ACTS_DISTRO): $(sort $(shell find $(LOCAL_PATH)/acts))
 	@echo "Packaging ACTS into $(ACTS_DISTRO)"
 	@mkdir -p $(HOST_OUT)/acts-dist/
 	@rm -f $(HOST_OUT)/acts-dist/acts.zip
-	$(hide) zip -r $(HOST_OUT)/acts-dist/acts.zip tools/test/connectivity/acts/*
+	$(hide) zip $(HOST_OUT)/acts-dist/acts.zip $(shell find tools/test/connectivity/acts/* ! -wholename "*__pycache__*")
 acts: $(ACTS_DISTRO)
 
 $(call dist-for-goals,tests,$(ACTS_DISTRO))
diff --git a/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py b/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py
index 6dbfd49..d03cf19 100644
--- a/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py
+++ b/acts/framework/acts/controllers/attenuator_lib/_tnhelper.py
@@ -19,6 +19,7 @@
 User code shouldn't need to directly access this class.
 """
 
+import logging
 import telnetlib
 from acts.controllers import attenuator
 
@@ -44,7 +45,7 @@
     def open(self, host, port=23):
         if self._tn:
             self._tn.close()
-
+        logging.debug("Attenuator IP = %s" % host)
         self._tn = telnetlib.Telnet()
         self._tn.open(host, port, 10)
 
diff --git a/acts/framework/acts/test_utils/bt/bt_contacts_utils.py b/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
index 1024c80..9d6b2c7 100644
--- a/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
@@ -425,3 +425,4 @@
         log.info(pce_call_log)
 
     return call_logs_match
+
diff --git a/acts/framework/acts/test_utils/bt/bt_test_utils.py b/acts/framework/acts/test_utils/bt/bt_test_utils.py
index d7e44f1..e35e2f1 100644
--- a/acts/framework/acts/test_utils/bt/bt_test_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_test_utils.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+# /usr/bin/env python3.4
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
@@ -421,7 +421,7 @@
 
         regex = "(" + adv_succ.format(
             advertise_callback) + "|" + adv_fail.format(
-                advertise_callback) + ")"
+            advertise_callback) + ")"
         # wait for either success or failure event
         evt = android_device.ed.pop_events(regex, bt_default_timeout,
                                            small_timeout)
@@ -474,7 +474,7 @@
         else:
             max_advertisements = determine_max_advertisements(a)
             max_tries = 3
-            #Retry to calculate max advertisements
+            # Retry to calculate max advertisements
             while max_advertisements == -1 and max_tries > 0:
                 a.log.info(
                     "Attempts left to determine max advertisements: {}".format(
@@ -497,7 +497,7 @@
 def generate_id_by_size(
         size,
         chars=(
-            string.ascii_lowercase + string.ascii_uppercase + string.digits)):
+                string.ascii_lowercase + string.ascii_uppercase + string.digits)):
     """Generate random ascii characters of input size and input char types
 
     Args:
@@ -537,7 +537,7 @@
     except Exception as err:
         adv_android_device.log.debug(
             "Failed to stop LE advertisement... reseting Bluetooth. Error {}".
-            format(err))
+                format(err))
         reset_bluetooth([adv_android_device])
 
 
@@ -902,7 +902,7 @@
     paired = False
     for paired_device in pri_ad.droid.bluetoothGetBondedDevices():
         if paired_device['address'] == \
-            sec_ad.droid.bluetoothGetLocalAddress():
+                sec_ad.droid.bluetoothGetLocalAddress():
             paired = True
             break
 
@@ -960,7 +960,7 @@
         device_addr = profile_event['data']['addr']
 
         if state == bt_profile_states['connected'] and \
-            device_addr == sec_ad.droid.bluetoothGetLocalAddress():
+                device_addr == sec_ad.droid.bluetoothGetLocalAddress():
             profile_connected.add(profile)
         pri_ad.log.info(
             "Profiles connected until now {}".format(profile_connected))
@@ -1020,7 +1020,7 @@
         device_addr = profile_event['data']['addr']
 
         if state == bt_profile_states['disconnected'] and \
-            device_addr == sec_ad.droid.bluetoothGetLocalAddress():
+                device_addr == sec_ad.droid.bluetoothGetLocalAddress():
             profile_disconnected.add(profile)
         pri_ad.log.info(
             "Profiles disconnected so far {}".format(profile_disconnected))
@@ -1252,6 +1252,11 @@
     if not toggle_airplane_mode_by_adb(log, panu_dut, True):
         panu_dut.log.error("Failed to toggle airplane mode on")
         return False
+    if not toggle_airplane_mode_by_adb(log, panu_dut, False):
+        pan_dut.log.error("Failed to toggle airplane mode off")
+        return False
+    pan_dut.droid.bluetoothStartConnectionStateChangeMonitor("")
+    panu_dut.droid.bluetoothStartConnectionStateChangeMonitor("")
     if not bluetooth_enabled_check(panu_dut):
         return False
     if not bluetooth_enabled_check(pan_dut):
@@ -1387,6 +1392,26 @@
                                                  hid_keyboard_report("00"))
 
 
+def is_a2dp_connected(sink, source):
+    """
+    Convenience Function to see if the 2 devices are connected on
+    A2dp.
+    Args:
+        sink:       Audio Sink
+        source:     Audio Source
+    Returns:
+        True if Connected
+        False if Not connected
+    """
+
+    devices = sink.droid.bluetoothA2dpSinkGetConnectedDevices()
+    for device in devices:
+        sink.log.info("A2dp Connected device {}".format(device["name"]))
+        if (device["address"] == source.droid.bluetoothGetLocalAddress()):
+            return True
+    return False
+
+
 def get_device_selector_dictionary(android_device_list):
     """Create a dictionary of Bluetooth features vs Android devices.
 
diff --git a/acts/framework/acts/test_utils/car/car_media_utils.py b/acts/framework/acts/test_utils/car/car_media_utils.py
index 6c7bc3d..3fb1e6c 100644
--- a/acts/framework/acts/test_utils/car/car_media_utils.py
+++ b/acts/framework/acts/test_utils/car/car_media_utils.py
@@ -84,26 +84,6 @@
     return verifyEventReceived(log, toDevice, expctEvent, timeout)
 
 
-def is_a2dp_connected(log, sink, source):
-    """
-    Convenience Function to see if the 2 devices are connected on
-    A2dp.
-    ToDo: Move to bt_test_utils if used in more places.
-    Args:
-        sink:       Audio Sink
-        source:     Audio Source
-    Returns:
-        True if Connected
-        False if Not connected
-    """
-    devices = sink.droid.bluetoothA2dpSinkGetConnectedDevices()
-    for device in devices:
-        log.info("A2dp Connected device {}".format(device["name"]))
-        if (device["address"] == source.droid.bluetoothGetLocalAddress()):
-            return True
-    return False
-
-
 def log_metadata(log, metadata):
     """
     Log the Metadata to the console.
@@ -154,7 +134,7 @@
         return False
 
     if not (metadata1[MEDIA_KEY_NUM_TRACKS] == metadata2[MEDIA_KEY_NUM_TRACKS]
-            ):
+    ):
         log.info("Song Num Tracks do not match")
         return False
 
diff --git a/acts/framework/acts/test_utils/tel/tel_test_utils.py b/acts/framework/acts/test_utils/tel/tel_test_utils.py
index d6896c7..e7a7609 100644
--- a/acts/framework/acts/test_utils/tel/tel_test_utils.py
+++ b/acts/framework/acts/test_utils/tel/tel_test_utils.py
@@ -2837,6 +2837,9 @@
 def trigger_modem_crash_by_modem(ad, timeout=120):
     begin_time = get_device_epoch_time(ad)
     ad.adb.shell(
+        "setprop persist.vendor.sys.modem.diag.mdlog false", ignore_status=True)
+    # Legacy pixels use persist.sys.modem.diag.mdlog.
+    ad.adb.shell(
         "setprop persist.sys.modem.diag.mdlog false", ignore_status=True)
     disable_qxdm_logger(ad)
     cmd = ('am instrument -w -e request "4b 25 03 00" '
diff --git a/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py b/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py
index dec26a2..9ca84d3 100644
--- a/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py
+++ b/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py
@@ -15,6 +15,7 @@
 #   limitations under the License.
 
 from acts import asserts
+from acts import utils
 from acts.base_test import BaseTestClass
 from acts.test_utils.wifi import wifi_test_utils as wutils
 from acts.test_utils.wifi.aware import aware_const as aconsts
@@ -23,7 +24,7 @@
 
 class AwareBaseTest(BaseTestClass):
   def __init__(self, controllers):
-    BaseTestClass.__init__(self, controllers)
+    super(AwareBaseTest, self).__init__(controllers)
 
   # message ID counter to make sure all uses are unique
   msg_id = 0
@@ -43,6 +44,7 @@
           "Device under test does not support Wi-Fi Aware - skipping test")
       wutils.wifi_toggle_state(ad, True)
       ad.droid.wifiP2pClose()
+      utils.set_location_service(ad, True)
       aware_avail = ad.droid.wifiIsAwareAvailable()
       if not aware_avail:
         self.log.info('Aware not available. Waiting ...')
@@ -52,7 +54,11 @@
       self.reset_device_parameters(ad)
       self.reset_device_statistics(ad)
       self.set_power_mode_parameters(ad)
-
+      ad.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+      autils.configure_ndp_allow_any_override(ad, True)
+      # set randomization interval to 0 (disable) to reduce likelihood of
+      # interference in tests
+      autils.configure_mac_random_interval(ad, 0)
 
   def teardown_test(self):
     for ad in self.android_devices:
@@ -85,9 +91,9 @@
     """Set the power configuration DW parameters for the device based on any
     configuration overrides (if provided)"""
     if self.aware_default_power_mode == "INTERACTIVE":
-      autils.config_dw_high_power(ad)
+      autils.config_settings_high_power(ad)
     elif self.aware_default_power_mode == "NON_INTERACTIVE":
-      autils.config_dw_low_power(ad)
+      autils.config_settings_low_power(ad)
     else:
       asserts.assert_false(
           "The 'aware_default_power_mode' configuration must be INTERACTIVE or "
@@ -102,3 +108,8 @@
     """
     self.msg_id = self.msg_id + 1
     return self.msg_id
+
+  def on_fail(self, test_name, begin_time):
+    for ad in self.android_devices:
+      ad.take_bug_report(test_name, begin_time)
+      ad.cat_adb_log(test_name, begin_time)
diff --git a/acts/framework/acts/test_utils/wifi/aware/aware_const.py b/acts/framework/acts/test_utils/wifi/aware/aware_const.py
index 608c5c7..36c469e 100644
--- a/acts/framework/acts/test_utils/wifi/aware/aware_const.py
+++ b/acts/framework/acts/test_utils/wifi/aware/aware_const.py
@@ -15,14 +15,21 @@
 #   limitations under the License.
 
 ######################################################
-# Aware DW (Discovery Window) power mode values
+# Aware power settings values for interactive (high power) and
+# non-interactive (low power) modes
 ######################################################
 
-DW_24_INTERACTIVE = 1
-DW_5_INTERACTIVE = 1
+POWER_DW_24_INTERACTIVE = 1
+POWER_DW_5_INTERACTIVE = 1
+POWER_DISC_BEACON_INTERVAL_INTERACTIVE = 0
+POWER_NUM_SS_IN_DISC_INTERACTIVE = 0
+POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE = 0
 
-DW_24_NON_INTERACTIVE = 4
-DW_5_NON_INTERACTIVE = 0
+POWER_DW_24_NON_INTERACTIVE = 4
+POWER_DW_5_NON_INTERACTIVE = 0
+POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE = 0
+POWER_NUM_SS_IN_DISC_NON_INTERACTIVE = 0
+POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE = 0
 
 ######################################################
 # Broadcast events
@@ -51,6 +58,9 @@
 DISCOVERY_KEY_DISCOVERY_TYPE = "DiscoveryType"
 DISCOVERY_KEY_TTL = "TtlSec"
 DISCOVERY_KEY_TERM_CB_ENABLED = "TerminateNotificationEnabled"
+DISCOVERY_KEY_RANGING_ENABLED = "RangingEnabled"
+DISCOVERY_KEY_MIN_DISTANCE_MM = "MinDistanceMm"
+DISCOVERY_KEY_MAX_DISTANCE_MM = "MaxDistanceMm"
 
 PUBLISH_TYPE_UNSOLICITED = 0
 PUBLISH_TYPE_SOLICITED = 1
@@ -101,6 +111,7 @@
 SESSION_CB_KEY_MESSAGE_AS_STRING = "messageAsString"
 SESSION_CB_KEY_LATENCY_MS = "latencyMs"
 SESSION_CB_KEY_TIMESTAMP_MS = "timestampMs"
+SESSION_CB_KEY_DISTANCE_MM = "distanceMm"
 
 ######################################################
 # WifiAwareRangingListener events (RttManager.RttListener)
diff --git a/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py b/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py
index 092df2b..ca15db5 100644
--- a/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py
@@ -310,21 +310,78 @@
   data_min = min(data)
   data_max = max(data)
   data_mean = statistics.mean(data)
+  data_cdf = extract_cdf(data)
+  data_cdf_decile = extract_cdf_decile(data_cdf)
 
   results['%smin' % key_prefix] = data_min
   results['%smax' % key_prefix] = data_max
   results['%smean' % key_prefix] = data_mean
+  results['%scdf' % key_prefix] = data_cdf
+  results['%scdf_decile' % key_prefix] = data_cdf_decile
   results['%sraw_data' % key_prefix] = data
 
   if num_samples > 1:
     data_stdev = statistics.stdev(data)
     results['%sstdev' % key_prefix] = data_stdev
-    ad.log.info('%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, stdev=%.2f',
-                log_prefix, num_samples, data_min, data_max, data_mean,
-                data_stdev)
+    ad.log.info(
+      '%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, stdev=%.2f, cdf_decile=%s',
+      log_prefix, num_samples, data_min, data_max, data_mean, data_stdev,
+      data_cdf_decile)
   else:
-    ad.log.info('%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f', log_prefix,
-                num_samples, data_min, data_max, data_mean)
+    ad.log.info(
+      '%s: num_samples=%d, min=%.2f, max=%.2f, mean=%.2f, cdf_decile=%s',
+      log_prefix, num_samples, data_min, data_max, data_mean, data_cdf_decile)
+
+def extract_cdf_decile(cdf):
+  """Extracts the 10%, 20%, ..., 90% points from the CDF and returns their
+  value (a list of 9 values).
+
+  Since CDF may not (will not) have exact x% value picks the value >= x%.
+
+  Args:
+    cdf: a list of 2 lists, the X and Y of the CDF.
+  """
+  decades = []
+  next_decade = 10
+  for x, y in zip(cdf[0], cdf[1]):
+    while 100*y >= next_decade:
+      decades.append(x)
+      next_decade = next_decade + 10
+    if next_decade == 100:
+      break
+  return decades
+
+def extract_cdf(data):
+  """Calculates the Cumulative Distribution Function (CDF) of the data.
+
+  Args:
+      data: A list containing data (does not have to be sorted).
+
+  Returns: a list of 2 lists: the X and Y axis of the CDF.
+  """
+  x = []
+  cdf = []
+  if not data:
+    return (x, cdf)
+
+  all_values = sorted(data)
+  for val in all_values:
+    if not x:
+      x.append(val)
+      cdf.append(1)
+    else:
+      if x[-1] == val:
+        cdf[-1] += 1
+      else:
+        x.append(val)
+        cdf.append(cdf[-1] + 1)
+
+  scale = 1.0 / len(all_values)
+  for i in range(len(cdf)):
+    cdf[i] = cdf[i] * scale
+
+  return (x, cdf)
+
 
 def get_mac_addr(device, interface):
   """Get the MAC address of the specified interface. Uses ifconfig and parses
@@ -392,74 +449,134 @@
   return dut.droid.wifiAwareCreateNetworkSpecifierOob(
       id, dev_type, peer_mac, None, sec)
 
-def configure_dw(device, is_default, is_24_band, value):
-  """Use the command-line API to configure the DW (discovery window) setting
+def configure_power_setting(device, mode, name, value):
+  """Use the command-line API to configure the power setting
 
   Args:
     device: Device on which to perform configuration
-    is_default: True for the default setting, False for the non-interactive
-                setting
-    is_24_band: True for 2.4GHz band, False for 5GHz band
-    value: An integer 0 to 5
+    mode: The power mode being set, should be "default", "inactive", or "idle"
+    name: One of the power settings from 'wifiaware set-power'.
+    value: An integer.
   """
-  variable = 'dw_%s_%sghz' % ('default' if is_default else 'on_inactive', '24'
-                              if is_24_band else '5')
-  device.adb.shell("cmd wifiaware native_api set %s %d" % (variable, value))
+  device.adb.shell(
+    "cmd wifiaware native_api set-power %s %s %d" % (mode, name, value))
 
-def config_dw_high_power(device):
-  """Configure device's discovery window (DW) values to high power mode -
+def configure_mac_random_interval(device, interval_sec):
+  """Use the command-line API to configure the MAC address randomization
+  interval.
+
+  Args:
+    device: Device on which to perform configuration
+    interval_sec: The MAC randomization interval in seconds. A value of 0
+                  disables all randomization.
+  """
+  device.adb.shell(
+    "cmd wifiaware native_api set mac_random_interval_sec %d" % interval_sec)
+
+def configure_ndp_allow_any_override(device, override_api_check):
+  """Use the command-line API to configure whether an NDP Responder may be
+  configured to accept an NDP request from ANY peer.
+
+  By default the target API level of the requesting app determines whether such
+  configuration is permitted. This allows overriding the API check and allowing
+  it.
+
+  Args:
+    device: Device on which to perform configuration.
+    override_api_check: True to allow a Responder to ANY configuration, False to
+                        perform the API level check.
+  """
+  device.adb.shell("cmd wifiaware state_mgr allow_ndp_any %s" % (
+    "true" if override_api_check else "false"))
+
+def config_settings_high_power(device):
+  """Configure device's power settings values to high power mode -
   whether device is in interactive or non-interactive modes"""
-  configure_dw(
-      device, is_default=True, is_24_band=True, value=aconsts.DW_24_INTERACTIVE)
-  configure_dw(
-      device, is_default=True, is_24_band=False, value=aconsts.DW_5_INTERACTIVE)
-  configure_dw(
-      device,
-      is_default=False,
-      is_24_band=True,
-      value=aconsts.DW_24_INTERACTIVE)
-  configure_dw(
-      device,
-      is_default=False,
-      is_24_band=False,
-      value=aconsts.DW_5_INTERACTIVE)
+  configure_power_setting(device, "default", "dw_24ghz",
+                          aconsts.POWER_DW_24_INTERACTIVE)
+  configure_power_setting(device, "default", "dw_5ghz",
+                          aconsts.POWER_DW_5_INTERACTIVE)
+  configure_power_setting(device, "default", "disc_beacon_interval_ms",
+                          aconsts.POWER_DISC_BEACON_INTERVAL_INTERACTIVE)
+  configure_power_setting(device, "default", "num_ss_in_discovery",
+                          aconsts.POWER_NUM_SS_IN_DISC_INTERACTIVE)
+  configure_power_setting(device, "default", "enable_dw_early_term",
+                          aconsts.POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE)
 
-def config_dw_low_power(device):
-  """Configure device's discovery window (DW) values to low power mode - whether
+  configure_power_setting(device, "inactive", "dw_24ghz",
+                          aconsts.POWER_DW_24_INTERACTIVE)
+  configure_power_setting(device, "inactive", "dw_5ghz",
+                          aconsts.POWER_DW_5_INTERACTIVE)
+  configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
+                          aconsts.POWER_DISC_BEACON_INTERVAL_INTERACTIVE)
+  configure_power_setting(device, "inactive", "num_ss_in_discovery",
+                          aconsts.POWER_NUM_SS_IN_DISC_INTERACTIVE)
+  configure_power_setting(device, "inactive", "enable_dw_early_term",
+                          aconsts.POWER_ENABLE_DW_EARLY_TERM_INTERACTIVE)
+
+def config_settings_low_power(device):
+  """Configure device's power settings values to low power mode - whether
   device is in interactive or non-interactive modes"""
-  configure_dw(
-      device,
-      is_default=True,
-      is_24_band=True,
-      value=aconsts.DW_24_NON_INTERACTIVE)
-  configure_dw(
-      device,
-      is_default=True,
-      is_24_band=False,
-      value=aconsts.DW_5_NON_INTERACTIVE)
-  configure_dw(
-      device,
-      is_default=False,
-      is_24_band=True,
-      value=aconsts.DW_24_NON_INTERACTIVE)
-  configure_dw(
-      device,
-      is_default=False,
-      is_24_band=False,
-      value=aconsts.DW_5_NON_INTERACTIVE)
+  configure_power_setting(device, "default", "dw_24ghz",
+                          aconsts.POWER_DW_24_NON_INTERACTIVE)
+  configure_power_setting(device, "default", "dw_5ghz",
+                          aconsts.POWER_DW_5_NON_INTERACTIVE)
+  configure_power_setting(device, "default", "disc_beacon_interval_ms",
+                          aconsts.POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE)
+  configure_power_setting(device, "default", "num_ss_in_discovery",
+                          aconsts.POWER_NUM_SS_IN_DISC_NON_INTERACTIVE)
+  configure_power_setting(device, "default", "enable_dw_early_term",
+                          aconsts.POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE)
 
-def config_dw_all_modes(device, dw_24ghz, dw_5ghz):
+  configure_power_setting(device, "inactive", "dw_24ghz",
+                          aconsts.POWER_DW_24_NON_INTERACTIVE)
+  configure_power_setting(device, "inactive", "dw_5ghz",
+                          aconsts.POWER_DW_5_NON_INTERACTIVE)
+  configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
+                          aconsts.POWER_DISC_BEACON_INTERVAL_NON_INTERACTIVE)
+  configure_power_setting(device, "inactive", "num_ss_in_discovery",
+                          aconsts.POWER_NUM_SS_IN_DISC_NON_INTERACTIVE)
+  configure_power_setting(device, "inactive", "enable_dw_early_term",
+                          aconsts.POWER_ENABLE_DW_EARLY_TERM_NON_INTERACTIVE)
+
+
+def config_power_settings(device, dw_24ghz, dw_5ghz, disc_beacon_interval=None,
+    num_ss_in_disc=None, enable_dw_early_term=None):
   """Configure device's discovery window (DW) values to the specified values -
   whether the device is in interactive or non-interactive mode.
 
   Args:
     dw_24ghz: DW interval in the 2.4GHz band.
     dw_5ghz: DW interval in the 5GHz band.
+    disc_beacon_interval: The discovery beacon interval (in ms). If None then
+                          not set.
+    num_ss_in_disc: Number of spatial streams to use for discovery. If None then
+                    not set.
+    enable_dw_early_term: If True then enable early termination of the DW. If
+                          None then not set.
   """
-  configure_dw(device, is_default=True, is_24_band=True, value=dw_24ghz)
-  configure_dw(device, is_default=True, is_24_band=False, value=dw_5ghz)
-  configure_dw(device, is_default=False, is_24_band=True, value=dw_24ghz)
-  configure_dw(device, is_default=False, is_24_band=False, value=dw_5ghz)
+  configure_power_setting(device, "default", "dw_24ghz", dw_24ghz)
+  configure_power_setting(device, "default", "dw_5ghz", dw_5ghz)
+  configure_power_setting(device, "inactive", "dw_24ghz", dw_24ghz)
+  configure_power_setting(device, "inactive", "dw_5ghz", dw_5ghz)
+
+  if disc_beacon_interval is not None:
+    configure_power_setting(device, "default", "disc_beacon_interval_ms",
+                            disc_beacon_interval)
+    configure_power_setting(device, "inactive", "disc_beacon_interval_ms",
+                            disc_beacon_interval)
+
+  if num_ss_in_disc is not None:
+    configure_power_setting(device, "default", "num_ss_in_discovery",
+                            num_ss_in_disc)
+    configure_power_setting(device, "inactive", "num_ss_in_discovery",
+                            num_ss_in_disc)
+
+  if enable_dw_early_term is not None:
+    configure_power_setting(device, "default", "enable_dw_early_term",
+                            enable_dw_early_term)
+    configure_power_setting(device, "inactive", "enable_dw_early_term",
+                            enable_dw_early_term)
 
 def create_discovery_config(service_name,
                           d_type,
@@ -495,6 +612,36 @@
   config[aconsts.DISCOVERY_KEY_TERM_CB_ENABLED] = term_cb_enable
   return config
 
+def add_ranging_to_pub(p_config, enable_ranging):
+  """Add ranging enabled configuration to a publish configuration (only relevant
+  for publish configuration).
+
+  Args:
+    p_config: The Publish discovery configuration.
+    enable_ranging: True to enable ranging, False to disable.
+  Returns:
+    The modified publish configuration.
+  """
+  p_config[aconsts.DISCOVERY_KEY_RANGING_ENABLED] = enable_ranging
+  return p_config
+
+def add_ranging_to_sub(s_config, min_distance_mm, max_distance_mm):
+  """Add ranging distance configuration to a subscribe configuration (only
+  relevant to a subscribe configuration).
+
+  Args:
+    s_config: The Subscribe discovery configuration.
+    min_distance_mm, max_distance_mm: The min and max distance specification.
+                                      Used if not None.
+  Returns:
+    The modified subscribe configuration.
+  """
+  if min_distance_mm is not None:
+    s_config[aconsts.DISCOVERY_KEY_MIN_DISTANCE_MM] = min_distance_mm
+  if max_distance_mm is not None:
+    s_config[aconsts.DISCOVERY_KEY_MAX_DISTANCE_MM] = max_distance_mm
+  return s_config
+
 def attach_with_identity(dut):
   """Start an Aware session (attach) and wait for confirmation and identity
   information (mac address).
diff --git a/acts/framework/acts/test_utils/wifi/rtt/RttBaseTest.py b/acts/framework/acts/test_utils/wifi/rtt/RttBaseTest.py
new file mode 100644
index 0000000..2182780
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/rtt/RttBaseTest.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2017 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts import asserts
+from acts import utils
+from acts.base_test import BaseTestClass
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+
+
+class RttBaseTest(BaseTestClass):
+
+  def __init__(self, controllers):
+    super(RttBaseTest, self).__init__(controllers)
+
+  def setup_test(self):
+    required_params = ("lci_reference", "lcr_reference",
+                       "rtt_reference_distance_mm",
+                       "stress_test_min_iteration_count",
+                       "stress_test_target_run_time_sec")
+    self.unpack_userparams(required_params)
+
+    # can be moved to JSON config file
+    self.rtt_reference_distance_margin_mm = 1000
+    self.rtt_max_failure_rate_two_sided_rtt_percentage = 10
+    self.rtt_max_failure_rate_one_sided_rtt_percentage = 50
+    self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage = 10
+    self.rtt_max_margin_exceeded_rate_one_sided_rtt_percentage = 50
+    self.rtt_min_expected_rssi_dbm = -100
+
+    for ad in self.android_devices:
+      utils.set_location_service(ad, True)
+      asserts.skip_if(
+          not ad.droid.doesDeviceSupportWifiRttFeature(),
+          "Device under test does not support Wi-Fi RTT - skipping test")
+      wutils.wifi_toggle_state(ad, True)
+      rtt_avail = ad.droid.wifiIsRttAvailable()
+      if not rtt_avail:
+          self.log.info('RTT not available. Waiting ...')
+          rutils.wait_for_event(ad, rconsts.BROADCAST_WIFI_RTT_AVAILABLE)
+      ad.ed.clear_all_events()
+      rutils.config_privilege_override(ad, False)
+      ad.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+
+  def teardown_test(self):
+    for ad in self.android_devices:
+      if not ad.droid.doesDeviceSupportWifiRttFeature():
+        return
+
+      # clean-up queue from the System Service UID
+      ad.droid.wifiRttCancelRanging([1000])
+
+  def on_fail(self, test_name, begin_time):
+    for ad in self.android_devices:
+      ad.take_bug_report(test_name, begin_time)
+      ad.cat_adb_log(test_name, begin_time)
diff --git a/acts/framework/acts/test_utils/wifi/rtt/__init__.py b/acts/framework/acts/test_utils/wifi/rtt/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/rtt/__init__.py
diff --git a/acts/framework/acts/test_utils/wifi/rtt/rtt_const.py b/acts/framework/acts/test_utils/wifi/rtt/rtt_const.py
new file mode 100644
index 0000000..ddf29e5
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/rtt/rtt_const.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2017 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+######################################################
+# Broadcast events
+######################################################
+BROADCAST_WIFI_RTT_AVAILABLE = "WifiRttAvailable"
+BROADCAST_WIFI_RTT_NOT_AVAILABLE = "WifiRttNotAvailable"
+
+######################################################
+# RangingResultCallback events
+######################################################
+EVENT_CB_RANGING_ON_FAIL = "WifiRttRangingFailure"
+EVENT_CB_RANGING_ON_RESULT = "WifiRttRangingResults"
+
+EVENT_CB_RANGING_KEY_RESULTS = "Results"
+
+EVENT_CB_RANGING_KEY_STATUS = "status"
+EVENT_CB_RANGING_KEY_DISTANCE_MM = "distanceMm"
+EVENT_CB_RANGING_KEY_DISTANCE_STD_DEV_MM = "distanceStdDevMm"
+EVENT_CB_RANGING_KEY_RSSI = "rssi"
+EVENT_CB_RANGING_KEY_NUM_ATTEMPTED_MEASUREMENTS = "numAttemptedMeasurements"
+EVENT_CB_RANGING_KEY_NUM_SUCCESSFUL_MEASUREMENTS = "numSuccessfulMeasurements"
+EVENT_CB_RANGING_KEY_LCI = "lci"
+EVENT_CB_RANGING_KEY_LCR = "lcr"
+EVENT_CB_RANGING_KEY_TIMESTAMP = "timestamp"
+EVENT_CB_RANGING_KEY_MAC = "mac"
+EVENT_CB_RANGING_KEY_PEER_ID = "peerId"
+EVENT_CB_RANGING_KEY_MAC_AS_STRING = "macAsString"
+
+EVENT_CB_RANGING_STATUS_SUCCESS = 0
+EVENT_CB_RANGING_STATUS_FAIL = 1
+EVENT_CB_RANGING_STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2
+
+######################################################
+# status codes
+######################################################
+
+RANGING_FAIL_CODE_GENERIC = 1
+RANGING_FAIL_CODE_RTT_NOT_AVAILABLE = 2
+
+######################################################
+# ScanResults keys
+######################################################
+
+SCAN_RESULT_KEY_RTT_RESPONDER = "is80211McRTTResponder"
\ No newline at end of file
diff --git a/acts/framework/acts/test_utils/wifi/rtt/rtt_test_utils.py b/acts/framework/acts/test_utils/wifi/rtt/rtt_test_utils.py
new file mode 100644
index 0000000..c24b406
--- /dev/null
+++ b/acts/framework/acts/test_utils/wifi/rtt/rtt_test_utils.py
@@ -0,0 +1,463 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2017 - Google
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import queue
+import statistics
+import time
+
+from acts import asserts
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+
+# arbitrary timeout for events
+EVENT_TIMEOUT = 10
+
+
+def decorate_event(event_name, id):
+  return '%s_%d' % (event_name, id)
+
+
+def wait_for_event(ad, event_name, timeout=EVENT_TIMEOUT):
+  """Wait for the specified event or timeout.
+
+  Args:
+    ad: The android device
+    event_name: The event to wait on
+    timeout: Number of seconds to wait
+  Returns:
+    The event (if available)
+  """
+  prefix = ''
+  if hasattr(ad, 'pretty_name'):
+    prefix = '[%s] ' % ad.pretty_name
+  try:
+    event = ad.ed.pop_event(event_name, timeout)
+    ad.log.info('%s%s: %s', prefix, event_name, event['data'])
+    return event
+  except queue.Empty:
+    ad.log.info('%sTimed out while waiting for %s', prefix, event_name)
+    asserts.fail(event_name)
+
+def fail_on_event(ad, event_name, timeout=EVENT_TIMEOUT):
+  """Wait for a timeout period and looks for the specified event - fails if it
+  is observed.
+
+  Args:
+    ad: The android device
+    event_name: The event to wait for (and fail on its appearance)
+  """
+  prefix = ''
+  if hasattr(ad, 'pretty_name'):
+    prefix = '[%s] ' % ad.pretty_name
+  try:
+    event = ad.ed.pop_event(event_name, timeout)
+    ad.log.info('%sReceived unwanted %s: %s', prefix, event_name, event['data'])
+    asserts.fail(event_name, extras=event)
+  except queue.Empty:
+    ad.log.info('%s%s not seen (as expected)', prefix, event_name)
+    return
+
+
+def config_privilege_override(dut, override_to_no_privilege):
+  """Configure the device to override the permission check and to disallow any
+  privileged RTT operations, e.g. disallow one-sided RTT to Responders (APs)
+  which do not support IEEE 802.11mc.
+
+  Args:
+    dut: Device to configure.
+    override_to_no_privilege: True to indicate no privileged ops, False for
+                              default (which will allow privileged ops).
+  """
+  dut.adb.shell("cmd wifirtt set override_assume_no_privilege %d" % (
+    1 if override_to_no_privilege else 0))
+
+
+def get_rtt_constrained_results(scanned_networks, support_rtt):
+  """Filter the input list and only return those networks which either support
+  or do not support RTT (IEEE 802.11mc.)
+
+  Args:
+    scanned_networks: A list of networks from scan results.
+      support_rtt: True - only return those APs which support RTT, False - only
+                   return those APs which do not support RTT.
+
+  Returns: a sub-set of the scanned_networks per support_rtt constraint.
+  """
+  matching_networks = []
+  for network in scanned_networks:
+    if support_rtt:
+      if (rconsts.SCAN_RESULT_KEY_RTT_RESPONDER in network and
+          network[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER]):
+        matching_networks.append(network)
+    else:
+      if (rconsts.SCAN_RESULT_KEY_RTT_RESPONDER not in network or
+            not network[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER]):
+        matching_networks.append(network)
+
+  return matching_networks
+
+
+def scan_networks(dut):
+  """Perform a scan and return scan results.
+
+  Args:
+    dut: Device under test.
+
+  Returns: an array of scan results.
+  """
+  wutils.start_wifi_connection_scan(dut)
+  return dut.droid.wifiGetScanResults()
+
+
+def scan_with_rtt_support_constraint(dut, support_rtt, repeat=0):
+  """Perform a scan and return scan results of APs: only those that support or
+  do not support RTT (IEEE 802.11mc) - per the support_rtt parameter.
+
+  Args:
+    dut: Device under test.
+    support_rtt: True - only return those APs which support RTT, False - only
+                 return those APs which do not support RTT.
+    repeat: Re-scan this many times to find an RTT supporting network.
+
+  Returns: an array of scan results.
+  """
+  for i in range(repeat + 1):
+    scan_results = scan_networks(dut)
+    aps = get_rtt_constrained_results(scan_results, support_rtt)
+    if len(aps) != 0:
+      return aps
+
+  return []
+
+
+def select_best_scan_results(scans, select_count, lowest_rssi=-80):
+  """Select the strongest 'select_count' scans in the input list based on
+  highest RSSI. Exclude all very weak signals, even if results in a shorter
+  list.
+
+  Args:
+    scans: List of scan results.
+    select_count: An integer specifying how many scans to return at most.
+    lowest_rssi: The lowest RSSI to accept into the output.
+  Returns: a list of the strongest 'select_count' scan results from the scans
+           list.
+  """
+  def takeRssi(element):
+    return element['level']
+
+  result = []
+  scans.sort(key=takeRssi, reverse=True)
+  for scan in scans:
+    if len(result) == select_count:
+      break
+    if scan['level'] < lowest_rssi:
+      break # rest are lower since we're sorted
+    result.append(scan)
+
+  return result
+
+
+def validate_ap_result(scan_result, range_result):
+  """Validate the range results:
+  - Successful if AP (per scan result) support 802.11mc (allowed to fail
+    otherwise)
+  - MAC of result matches the BSSID
+
+  Args:
+    scan_result: Scan result for the AP
+    range_result: Range result returned by the RTT API
+  """
+  asserts.assert_equal(scan_result[wutils.WifiEnums.BSSID_KEY], range_result[
+    rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING_BSSID], 'MAC/BSSID mismatch')
+  if (rconsts.SCAN_RESULT_KEY_RTT_RESPONDER in scan_result and
+      scan_result[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER]):
+    asserts.assert_true(range_result[rconsts.EVENT_CB_RANGING_KEY_STATUS] ==
+                        rconsts.EVENT_CB_RANGING_STATUS_SUCCESS,
+                        'Ranging failed for an AP which supports 802.11mc!')
+
+
+def validate_ap_results(scan_results, range_results):
+  """Validate an array of ranging results against the scan results used to
+  trigger the range. The assumption is that the results are returned in the
+  same order as the request (which were the scan results).
+
+  Args:
+    scan_results: Scans results used to trigger the range request
+    range_results: Range results returned by the RTT API
+  """
+  asserts.assert_equal(
+      len(scan_results),
+      len(range_results),
+      'Mismatch in length of scan results and range results')
+
+  # sort first based on BSSID/MAC
+  scan_results.sort(key=lambda x: x[wutils.WifiEnums.BSSID_KEY])
+  range_results.sort(
+      key=lambda x: x[rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING_BSSID])
+
+  for i in range(len(scan_results)):
+    validate_ap_result(scan_results[i], range_results[i])
+
+
+def validate_aware_mac_result(range_result, mac, description):
+  """Validate the range result for an Aware peer specified with a MAC address:
+  - Correct MAC address.
+
+  The MAC addresses may contain ":" (which are ignored for the comparison) and
+  may be in any case (which is ignored for the comparison).
+
+  Args:
+    range_result: Range result returned by the RTT API
+    mac: MAC address of the peer
+    description: Additional content to print on failure
+  """
+  mac1 = mac.replace(':', '').lower()
+  mac2 = range_result[rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING].replace(':',
+                                                                  '').lower()
+  asserts.assert_equal(mac1, mac2,
+                       '%s: MAC mismatch' % description)
+
+def validate_aware_peer_id_result(range_result, peer_id, description):
+  """Validate the range result for An Aware peer specified with a Peer ID:
+  - Correct Peer ID
+  - MAC address information not available
+
+  Args:
+    range_result: Range result returned by the RTT API
+    peer_id: Peer ID of the peer
+    description: Additional content to print on failure
+  """
+  asserts.assert_equal(peer_id,
+                       range_result[rconsts.EVENT_CB_RANGING_KEY_PEER_ID],
+                       '%s: Peer Id mismatch' % description)
+  asserts.assert_false(rconsts.EVENT_CB_RANGING_KEY_MAC in range_result,
+                       '%s: MAC Address not empty!' % description)
+
+
+def extract_stats(results, range_reference_mm, range_margin_mm, min_rssi,
+    reference_lci=[], reference_lcr=[], summary_only=False):
+  """Extract statistics from a list of RTT results. Returns a dictionary
+   with results:
+     - num_results (success or fails)
+     - num_success_results
+     - num_no_results (e.g. timeout)
+     - num_failures
+     - num_range_out_of_margin (only for successes)
+     - num_invalid_rssi (only for successes)
+     - distances: extracted list of distances
+     - distance_std_devs: extracted list of distance standard-deviations
+     - rssis: extracted list of RSSI
+     - distance_mean
+     - distance_std_dev (based on distance - ignoring the individual std-devs)
+     - rssi_mean
+     - rssi_std_dev
+     - status_codes
+     - lcis: extracted list of all of the individual LCI
+     - lcrs: extracted list of all of the individual LCR
+     - any_lci_mismatch: True/False - checks if all LCI results are identical to
+                         the reference LCI.
+     - any_lcr_mismatch: True/False - checks if all LCR results are identical to
+                         the reference LCR.
+     - num_attempted_measurements: extracted list of all of the individual
+                                   number of attempted measurements.
+     - num_successful_measurements: extracted list of all of the individual
+                                    number of successful measurements.
+     - invalid_num_attempted: True/False - checks if number of attempted
+                              measurements is non-zero for successful results.
+     - invalid_num_successful: True/False - checks if number of successful
+                               measurements is non-zero for successful results.
+
+  Args:
+    results: List of RTT results.
+    range_reference_mm: Reference value for the distance (in mm)
+    range_margin_mm: Acceptable absolute margin for distance (in mm)
+    min_rssi: Acceptable minimum RSSI value.
+    reference_lci, reference_lcr: Reference values for LCI and LCR.
+    summary_only: Only include summary keys (reduce size).
+
+  Returns: A dictionary of stats.
+  """
+  stats = {}
+  stats['num_results'] = 0
+  stats['num_success_results'] = 0
+  stats['num_no_results'] = 0
+  stats['num_failures'] = 0
+  stats['num_range_out_of_margin'] = 0
+  stats['num_invalid_rssi'] = 0
+  stats['any_lci_mismatch'] = False
+  stats['any_lcr_mismatch'] = False
+  stats['invalid_num_attempted'] = False
+  stats['invalid_num_successful'] = False
+
+  range_max_mm = range_reference_mm + range_margin_mm
+  range_min_mm = range_reference_mm - range_margin_mm
+
+  distances = []
+  distance_std_devs = []
+  rssis = []
+  num_attempted_measurements = []
+  num_successful_measurements = []
+  status_codes = []
+  lcis = []
+  lcrs = []
+
+  for i in range(len(results)):
+    result = results[i]
+
+    if result is None: # None -> timeout waiting for RTT result
+      stats['num_no_results'] = stats['num_no_results'] + 1
+      continue
+    stats['num_results'] = stats['num_results'] + 1
+
+    status_codes.append(result[rconsts.EVENT_CB_RANGING_KEY_STATUS])
+    if status_codes[-1] != rconsts.EVENT_CB_RANGING_STATUS_SUCCESS:
+      stats['num_failures'] = stats['num_failures'] + 1
+      continue
+    stats['num_success_results'] = stats['num_success_results'] + 1
+
+    distance_mm = result[rconsts.EVENT_CB_RANGING_KEY_DISTANCE_MM]
+    distances.append(distance_mm)
+    if not range_min_mm <= distance_mm <= range_max_mm:
+      stats['num_range_out_of_margin'] = stats['num_range_out_of_margin'] + 1
+    distance_std_devs.append(
+        result[rconsts.EVENT_CB_RANGING_KEY_DISTANCE_STD_DEV_MM])
+
+    rssi = result[rconsts.EVENT_CB_RANGING_KEY_RSSI]
+    rssis.append(rssi)
+    if not min_rssi <= rssi <= 0:
+      stats['num_invalid_rssi'] = stats['num_invalid_rssi'] + 1
+
+    num_attempted = result[
+      rconsts.EVENT_CB_RANGING_KEY_NUM_ATTEMPTED_MEASUREMENTS]
+    num_attempted_measurements.append(num_attempted)
+    if num_attempted == 0:
+      stats['invalid_num_attempted'] = True
+
+    num_successful = result[
+      rconsts.EVENT_CB_RANGING_KEY_NUM_SUCCESSFUL_MEASUREMENTS]
+    num_successful_measurements.append(num_successful)
+    if num_successful == 0:
+      stats['invalid_num_successful'] = True
+
+    lcis.append(result[rconsts.EVENT_CB_RANGING_KEY_LCI])
+    if (result[rconsts.EVENT_CB_RANGING_KEY_LCI] != reference_lci):
+      stats['any_lci_mismatch'] = True
+    lcrs.append(result[rconsts.EVENT_CB_RANGING_KEY_LCR])
+    if (result[rconsts.EVENT_CB_RANGING_KEY_LCR] != reference_lcr):
+      stats['any_lcr_mismatch'] = True
+
+  if len(distances) > 0:
+    stats['distance_mean'] = statistics.mean(distances)
+  if len(distances) > 1:
+    stats['distance_std_dev'] = statistics.stdev(distances)
+  if len(rssis) > 0:
+    stats['rssi_mean'] = statistics.mean(rssis)
+  if len(rssis) > 1:
+    stats['rssi_std_dev'] = statistics.stdev(rssis)
+  if not summary_only:
+    stats['distances'] = distances
+    stats['distance_std_devs'] = distance_std_devs
+    stats['rssis'] = rssis
+    stats['num_attempted_measurements'] = num_attempted_measurements
+    stats['num_successful_measurements'] = num_successful_measurements
+    stats['status_codes'] = status_codes
+    stats['lcis'] = lcis
+    stats['lcrs'] = lcrs
+
+  return stats
+
+
+def run_ranging(dut, aps, iter_count, time_between_iterations,
+    target_run_time_sec=0):
+  """Executing ranging to the set of APs.
+
+  Will execute a minimum of 'iter_count' iterations. Will continue to run
+  until execution time (just) exceeds 'target_run_time_sec'.
+
+  Args:
+    dut: Device under test
+    aps: A list of APs (Access Points) to range to.
+    iter_count: (Minimum) Number of measurements to perform.
+    time_between_iterations: Number of seconds to wait between iterations.
+    target_run_time_sec: The target run time in seconds.
+
+  Returns: a list of the events containing the RTT results (or None for a
+  failed measurement).
+  """
+  max_peers = dut.droid.wifiRttMaxPeersInRequest()
+
+  asserts.assert_true(len(aps) > 0, "Need at least one AP!")
+  if len(aps) > max_peers:
+    aps = aps[0:max_peers]
+
+  events = {} # need to keep track per BSSID!
+  for ap in aps:
+    events[ap["BSSID"]] = []
+
+  start_clock = time.time()
+  iterations_done = 0
+  run_time = 0
+  while iterations_done < iter_count or (
+      target_run_time_sec != 0 and run_time < target_run_time_sec):
+    if iterations_done != 0 and time_between_iterations != 0:
+      time.sleep(time_between_iterations)
+
+    id = dut.droid.wifiRttStartRangingToAccessPoints(aps)
+    try:
+      event = dut.ed.pop_event(
+        decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT, id), EVENT_TIMEOUT)
+      range_results = event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS]
+      asserts.assert_equal(
+          len(aps),
+          len(range_results),
+          'Mismatch in length of scan results and range results')
+      for result in range_results:
+        bssid = result[rconsts.EVENT_CB_RANGING_KEY_MAC_AS_STRING]
+        asserts.assert_true(bssid in events,
+                            "Result BSSID %s not in requested AP!?" % bssid)
+        asserts.assert_equal(len(events[bssid]), iterations_done,
+                             "Duplicate results for BSSID %s!?" % bssid)
+        events[bssid].append(result)
+    except queue.Empty:
+      for ap in aps:
+        events[ap["BSSID"]].append(None)
+
+    iterations_done = iterations_done + 1
+    run_time = time.time() - start_clock
+
+  return events
+
+
+def analyze_results(all_aps_events, rtt_reference_distance_mm,
+    distance_margin_mm, min_expected_rssi, lci_reference, lcr_reference,
+    summary_only=False):
+  """Verifies the results of the RTT experiment.
+
+  Args:
+    all_aps_events: Dictionary of APs, each a list of RTT result events.
+    rtt_reference_distance_mm: Expected distance to the AP (source of truth).
+    distance_margin_mm: Accepted error marging in distance measurement.
+    min_expected_rssi: Minimum acceptable RSSI value
+    lci_reference, lcr_reference: Expected LCI/LCR values (arrays of bytes).
+    summary_only: Only include summary keys (reduce size).
+  """
+  all_stats = {}
+  for bssid, events in all_aps_events.items():
+    stats = extract_stats(events, rtt_reference_distance_mm,
+                          distance_margin_mm, min_expected_rssi,
+                          lci_reference, lcr_reference, summary_only)
+    all_stats[bssid] = stats
+  return all_stats
diff --git a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
index b6510ae..9b17383 100755
--- a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
+++ b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py
@@ -234,6 +234,10 @@
     REPORT_EVENT_AFTER_EACH_SCAN = 1
     REPORT_EVENT_FULL_SCAN_RESULT = 2
 
+    SCAN_TYPE_LOW_LATENCY = 0
+    SCAN_TYPE_LOW_POWER = 1
+    SCAN_TYPE_HIGH_ACCURACY = 2
+
     # US Wifi frequencies
     ALL_2G_FREQUENCIES = [2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452,
                           2457, 2462]
diff --git a/acts/framework/acts/utils.py b/acts/framework/acts/utils.py
index d89e1d5..7181921 100755
--- a/acts/framework/acts/utils.py
+++ b/acts/framework/acts/utils.py
@@ -738,6 +738,12 @@
             If new_state is False, turn off location service.
             If new_state if True, set location service to "High accuracy".
     """
+    ad.adb.shell("content insert --uri "
+                 " content://com.google.settings/partner --bind "
+                 "name:s:network_location_opt_in --bind value:s:1")
+    ad.adb.shell("content insert --uri "
+                 " content://com.google.settings/partner --bind "
+                 "name:s:use_location_for_services --bind value:s:1")
     if new_state:
         ad.adb.shell("settings put secure location_providers_allowed +gps")
         ad.adb.shell("settings put secure location_providers_allowed +network")
@@ -759,16 +765,6 @@
         1 if new_state else 0))
 
 
-def set_regulatory_domain(ad, domain):
-    """Set the Wi-Fi regulatory domain
-
-    Args:
-      ad: android device object.
-      domain: regulatory domain
-    """
-    ad.adb.shell("iw reg set %s" % domain)
-
-
 def bypass_setup_wizard(ad, bypass_wait_time=3):
     """Bypass the setup wizard on an input Android device
 
diff --git a/acts/framework/tests/acts_android_device_test.py b/acts/framework/tests/acts_android_device_test.py
index 2052281..08332ab 100755
--- a/acts/framework/tests/acts_android_device_test.py
+++ b/acts/framework/tests/acts_android_device_test.py
@@ -286,6 +286,26 @@
     @mock.patch(
         'acts.controllers.fastboot.FastbootProxy',
         return_value=MockFastbootProxy(MOCK_SERIAL))
+    def test_AndroidDevice_build_info_release(self, MockFastboot,
+                                              MockAdbProxy):
+        """Verifies the AndroidDevice object's basic attributes are correctly
+        set after instantiation.
+        """
+        global MOCK_BUILD_ID
+        ad = android_device.AndroidDevice(serial=1)
+        old_mock_build_id = MOCK_BUILD_ID
+        MOCK_BUILD_ID = "ABC-MR1"
+        build_info = ad.build_info
+        self.assertEqual(build_info["build_id"], "123456789")
+        self.assertEqual(build_info["build_type"], "userdebug")
+        MOCK_BUILD_ID = old_mock_build_id
+
+    @mock.patch(
+        'acts.controllers.adb.AdbProxy',
+        return_value=MockAdbProxy(MOCK_SERIAL))
+    @mock.patch(
+        'acts.controllers.fastboot.FastbootProxy',
+        return_value=MockFastbootProxy(MOCK_SERIAL))
     def test_AndroidDevice_build_info_dev(self, MockFastboot, MockAdbProxy):
         """Verifies the AndroidDevice object's basic attributes are correctly
         set after instantiation.
@@ -315,6 +335,7 @@
     @mock.patch(
         'acts.controllers.fastboot.FastbootProxy',
         return_value=MockFastbootProxy(MOCK_SERIAL))
+
     @mock.patch('acts.utils.create_dir')
     @mock.patch('acts.utils.exe_cmd')
     def test_AndroidDevice_take_bug_report(self, exe_mock, create_dir_mock,
diff --git a/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py b/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
index 3841883..4611c0d 100644
--- a/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarMediaConnectionTest.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+# /usr/bin/env python3.4
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
@@ -23,8 +23,8 @@
 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
 from acts.test_utils.bt import bt_test_utils
 from acts.test_utils.car import car_bt_utils
-from acts.test_utils.car import car_media_utils
 from acts.test_utils.bt import BtEnum
+from acts.test_utils.bt.bt_test_utils import is_a2dp_connected
 
 
 class BtCarMediaConnectionTest(BluetoothBaseTest):
@@ -58,26 +58,6 @@
             self.SNK, self.SRC, [BtEnum.BluetoothProfile.A2DP_SINK],
             BtEnum.BluetoothPriorityLevel.PRIORITY_ON)
 
-    def is_a2dp_connected(self, device1, device2):
-        """
-        Convenience Function to see if the 2 devices are connected on
-        A2dp.
-        ToDo: Move to bt_test_utils if used in more places.
-        Args:
-            device1:    Device 1
-            device2:    Device 2
-        Returns:
-            True if Connected
-            False if Not connected
-        """
-        devices = device1.droid.bluetoothA2dpSinkGetConnectedDevices()
-        for device in devices:
-            self.device1.log.info("A2dp Connected device {}".format(device[
-                "name"]))
-            if (device["address"] == device2.droid.bluetoothGetLocalAddress()):
-                return True
-        return False
-
     @test_tracker_info(uuid='1934c0d5-3fa3-43e5-a91f-2c8a4424f5cd')
     @BluetoothBaseTest.bt_test_wrap
     def test_a2dp_connect_disconnect_from_src(self):
@@ -99,7 +79,7 @@
 
         Priority: 0
         """
-        if (car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC)):
+        if (is_a2dp_connected(self.SNK, self.SRC)):
             self.log.info("Already Connected")
         else:
             if (not bt_test_utils.connect_pri_to_sec(
@@ -120,7 +100,7 @@
                 return False
         # Logging if we connected right back, since that happens sometimes
         # Not failing the test if it did though
-        if (car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC)):
+        if (is_a2dp_connected(self.SNK, self.SRC)):
             self.SNK.log.error("Still connected after a disconnect")
 
         return True
@@ -147,7 +127,7 @@
         Priority: 0
         """
         # Connect
-        if car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC):
+        if is_a2dp_connected(self.SNK, self.SRC):
             self.log.info("Already Connected")
         else:
             if (not bt_test_utils.connect_pri_to_sec(
@@ -169,6 +149,6 @@
                 return False
         # Logging if we connected right back, since that happens sometimes
         # Not failing the test if it did though
-        if car_media_utils.is_a2dp_connected(self.log, self.SNK, self.SRC):
+        if is_a2dp_connected(self.SNK, self.SRC):
             self.SNK.log.error("Still connected after a disconnect")
         return True
diff --git a/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py b/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
index 05d1737..3e5b28f 100644
--- a/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarMediaPassthroughTest.py
@@ -25,8 +25,9 @@
 from acts.test_utils.bt import bt_test_utils
 from acts.test_utils.bt import BtEnum
 from acts.test_utils.car import car_media_utils
-from acts.utils import exe_cmd
-from acts.controllers import adb
+from acts.test_utils.bt.bt_test_utils import is_a2dp_connected
+from acts.keys import Config
+
 
 DEFAULT_WAIT_TIME = 1.0
 DEFAULT_EVENT_TIMEOUT = 1.0
@@ -126,8 +127,7 @@
         if not super(BtCarMediaPassthroughTest, self).teardown_test():
             return False
         # If A2dp connection was disconnected as part of the test, connect it back
-        if not (car_media_utils.is_a2dp_connected(self.log, self.SNK,
-                                                  self.SRC)):
+        if not (is_a2dp_connected(self.SNK,self.SRC)):
             result = bt_test_utils.connect_pri_to_sec(
                 self.SRC, self.SNK, set([BtEnum.BluetoothProfile.A2DP.value]))
             if not result:
@@ -235,8 +235,7 @@
 
         Priority: 0
         """
-        if not (car_media_utils.is_a2dp_connected(self.log, self.SNK,
-                                                  self.SRC)):
+        if not (is_a2dp_connected(self.SNK,self.SRC)):
             self.SNK.log.error('No A2dp Connection')
             return False
 
diff --git a/acts/tests/google/bt/car_bt/BtCarPbapTest.py b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
index 1a1bba3..c77c859 100644
--- a/acts/tests/google/bt/car_bt/BtCarPbapTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
@@ -485,3 +485,4 @@
         bt_contacts_utils.erase_contacts(self.pse)
         bt_contacts_utils.erase_contacts(self.pse2)
         return pse1_matches and pse2_matches and pse1andpse2_matches
+
diff --git a/acts/tests/google/wifi/WifiAutoUpdateTest.py b/acts/tests/google/wifi/WifiAutoUpdateTest.py
new file mode 100755
index 0000000..4a8ab7b
--- /dev/null
+++ b/acts/tests/google/wifi/WifiAutoUpdateTest.py
@@ -0,0 +1,242 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import itertools
+import pprint
+import queue
+import time
+
+import acts.base_test
+import acts.signals as signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils
+
+from acts import asserts
+from acts.libs.ota import ota_updater
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+# Default timeout used for reboot, toggle WiFi and Airplane mode,
+# for the system to settle down after the operation.
+DEFAULT_TIMEOUT = 10
+BAND_2GHZ = 0
+BAND_5GHZ = 1
+
+
+class WifiAutoUpdateTest(WifiBaseTest):
+    """Tests for APIs in Android's WifiManager class.
+
+    Test Bed Requirement:
+    * One Android device
+    * Several Wi-Fi networks visible to the device, including an open Wi-Fi
+      network.
+    """
+
+    def __init__(self, controllers):
+        WifiBaseTest.__init__(self, controllers)
+        self.tests = (
+            "test_check_wifi_state_after_au",
+            "test_verify_networks_after_au",
+            "test_all_networks_connectable_after_au",
+            "test_connection_to_new_networks",
+            "test_check_wifi_toggling_after_au",
+            "test_reset_wifi_after_au")
+
+    def setup_class(self):
+        super(WifiAutoUpdateTest, self).setup_class()
+        ota_updater.initialize(self.user_params, self.android_devices)
+        self.dut = self.android_devices[0]
+        wutils.wifi_test_device_init(self.dut)
+        req_params = []
+        opt_param = [
+            "open_network", "reference_networks", "iperf_server_address"
+        ]
+        self.unpack_userparams(
+            req_param_names=req_params, opt_param_names=opt_param)
+
+        if "AccessPoint" in self.user_params:
+            self.legacy_configure_ap_and_start()
+
+        asserts.assert_true(
+            len(self.reference_networks) > 0,
+            "Need at least two reference network with psk.")
+        asserts.assert_true(
+            len(self.open_network) > 0,
+            "Need at least two open network with psk.")
+        wutils.wifi_toggle_state(self.dut, True)
+
+        self.wifi_config_list = []
+
+        # Setup WiFi and add few open and wpa networks before OTA.
+        self.add_network_and_enable(self.open_network[0]['2g'])
+        self.add_network_and_enable(self.reference_networks[0]['5g'])
+
+        # Add few dummy networks to the list.
+        self.add_and_enable_dummy_networks()
+
+        # Run OTA below, if ota fails then abort all tests.
+        try:
+            ota_updater.update(self.dut)
+        except Exception as err:
+            raise signals.TestSkipClass(
+                "Failed up apply OTA update. Aborting tests")
+
+    def setup_test(self):
+        self.dut.droid.wakeLockAcquireBright()
+        self.dut.droid.wakeUpNow()
+
+    def teardown_test(self):
+        self.dut.droid.wakeLockRelease()
+        self.dut.droid.goToSleepNow()
+
+    def on_fail(self, test_name, begin_time):
+        self.dut.take_bug_report(test_name, begin_time)
+        self.dut.cat_adb_log(test_name, begin_time)
+
+    def teardown_class(self):
+        if "AccessPoint" in self.user_params:
+            del self.user_params["reference_networks"]
+            del self.user_params["open_network"]
+
+    """Helper Functions"""
+
+    def add_network_and_enable(self, network):
+        """Add a network and enable it.
+
+        Args:
+            network : Network details for the network to be added.
+
+        """
+        ret = self.dut.droid.wifiAddNetwork(network)
+        asserts.assert_true(ret != -1, "Add network %r failed" % network)
+        self.wifi_config_list.append({
+                WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY],
+                WifiEnums.NETID_KEY: ret})
+        self.dut.droid.wifiEnableNetwork(ret, 0)
+
+    def add_and_enable_dummy_networks(self, num_networks=5):
+        """Add some dummy networks to the device and enable them.
+
+        Args:
+            num_networks: Number of networks to add.
+        """
+        ssid_name_base = "dummy_network_"
+        for i in range(0, num_networks):
+            network = {}
+            network[WifiEnums.SSID_KEY] = ssid_name_base + str(i)
+            network[WifiEnums.PWD_KEY] = "dummynet_password"
+            self.add_network_and_enable(network)
+
+    def check_networks_after_autoupdate(self, networks):
+        """Verify that all previously configured networks are presistent after
+           reboot.
+
+        Args:
+            networks: List of network dicts.
+
+        Return:
+            None. Raises TestFailure.
+
+        """
+        network_info = self.dut.droid.wifiGetConfiguredNetworks()
+        if len(network_info) != len(networks):
+            msg = (
+                "Number of configured networks before and after Auto-update "
+                "don't match. \nBefore reboot = %s \n After reboot = %s" %
+                (networks, network_info))
+            raise signals.TestFailure(msg)
+        current_count = 0
+        # For each network, check if it exists in configured list after Auto-
+        # update.
+        for network in networks:
+            exists = wutils.match_networks({
+                WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY]
+            }, network_info)
+            if not len(exists):
+                raise signals.TestFailure("%s network is not present in the"
+                                          " configured list after Auto-update" %
+                                          network[WifiEnums.SSID_KEY])
+            # Get the new network id for each network after reboot.
+            network[WifiEnums.NETID_KEY] = exists[0]['networkId']
+
+    """Tests"""
+
+    @test_tracker_info(uuid="9ff1f01e-e5ff-408b-9a95-29e87a2df2d8")
+    def test_check_wifi_state_after_au(self):
+        """Check if the state of WiFi is enabled after Auto-update."""
+        if not self.dut.droid.wifiCheckState():
+            raise signals.TestFailure("WiFi is disabled after Auto-update!!!")
+
+    @test_tracker_info(uuid="e3ebdbba-71dd-4281-aef8-5b3d42b88770")
+    def test_verify_networks_after_au(self):
+        """Check if the previously added networks are intact.
+
+           Steps:
+               Number of networs should be the same and match each network.
+
+        """
+        self.check_networks_after_autoupdate(self.wifi_config_list)
+
+    @test_tracker_info(uuid="b8e47a4f-62fe-4a0e-b999-27ae1ebf4d19")
+    def test_connection_to_new_networks(self):
+        """Check if we can connect to new networks after Auto-update.
+
+           Steps:
+               1. Connect to a PSK network.
+               2. Connect to an open network.
+               3. Forget ntworks added in 1 & 2.
+               TODO: (@bmahadev) Add WEP network once it's ready.
+
+        """
+        wutils.connect_to_wifi_network((self.open_network[0]['5g'], self.dut))
+        wutils.connect_to_wifi_network((self.reference_networks[0]['2g'],
+                self.dut))
+        wutils.wifi_forget_network(self.dut,
+                self.reference_networks[0]['2g'][WifiEnums.SSID_KEY])
+        wutils.wifi_forget_network(self.dut,
+                self.open_network[0]['5g'][WifiEnums.SSID_KEY])
+
+    @test_tracker_info(uuid="1d8309e4-d5a2-4f48-ba3b-895a58c9bf3a")
+    def test_all_networks_connectable_after_au(self):
+        """Check if previously added networks are connectable.
+
+           Steps:
+               1. Connect to previously added PSK network using network id.
+               2. Connect to previously added open network using network id.
+               TODO: (@bmahadev) Add WEP network once it's ready.
+
+        """
+        for network in self.wifi_config_list:
+            if 'dummy' not in network[WifiEnums.SSID_KEY]:
+                if not wutils.connect_to_wifi_network_with_id(self.dut,
+                        network[WifiEnums.NETID_KEY],
+                        network[WifiEnums.SSID_KEY]):
+                    raise signals.TestFailure("Failed to connect to %s after \
+                            Auto-update" % network[WifiEnums.SSID_KEY])
+
+    @test_tracker_info(uuid="05671859-38b1-4dbf-930c-18048971d075")
+    def test_check_wifi_toggling_after_au(self):
+        """Check if WiFi can be toggled ON/OFF after auto-update."""
+        self.log.debug("Going from on to off.")
+        wutils.wifi_toggle_state(self.dut, False)
+        self.log.debug("Going from off to on.")
+        wutils.wifi_toggle_state(self.dut, True)
+
+    @test_tracker_info(uuid="440edf32-4b00-42b0-9811-9f2bc4a83efb")
+    def test_reset_wifi_after_au(self):
+        """"Check if WiFi can be reset after auto-update."""
+        wutils.reset_wifi(self.dut)
diff --git a/acts/tests/google/wifi/WifiDiagnosticsTest.py b/acts/tests/google/wifi/WifiDiagnosticsTest.py
new file mode 100644
index 0000000..79fb082
--- /dev/null
+++ b/acts/tests/google/wifi/WifiDiagnosticsTest.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3.4
+#
+#   Copyright 2018 - 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 itertools
+import pprint
+import queue
+import time
+
+import acts.base_test
+import acts.signals as signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+DEFAULT_WAIT_TIME = 2
+
+
+class WifiDiagnosticsTest(WifiBaseTest):
+    """
+    Test Bed Requirement:
+    * One Android device
+    * An open Wi-Fi network.
+    * Verbose logging is on.
+    """
+
+    def __init__(self, controllers):
+        WifiBaseTest.__init__(self, controllers)
+
+    def setup_class(self):
+        self.dut = self.android_devices[0]
+        wutils.wifi_test_device_init(self.dut)
+        req_params = []
+        opt_param = ["open_network"]
+        self.unpack_userparams(
+            req_param_names=req_params, opt_param_names=opt_param)
+
+        if "AccessPoint" in self.user_params:
+            self.legacy_configure_ap_and_start()
+        wutils.wifi_toggle_state(self.dut, True)
+        asserts.assert_true(
+            len(self.open_network) > 0,
+            "Need at least one open network.")
+        self.open_network = self.open_network[0]["2g"]
+
+    def setup_test(self):
+        self.dut.droid.wakeLockAcquireBright()
+        self.dut.droid.wakeUpNow()
+
+    def teardown_test(self):
+        self.dut.droid.wakeLockRelease()
+        self.dut.droid.goToSleepNow()
+        wutils.reset_wifi(self.dut)
+
+
+    def on_fail(self, test_name, begin_time):
+        self.dut.take_bug_report(test_name, begin_time)
+        self.dut.cat_adb_log(test_name, begin_time)
+
+    def teardown_class(self):
+        if "AccessPoint" in self.user_params:
+            del self.user_params["open_network"]
+
+    """Tests"""
+
+    @test_tracker_info(uuid="d6f1661b-6732-4939-8c28-f20917774ec0")
+    def test_ringbuffers_are_dumped_during_lsdebug(self):
+        """Steps:
+        1. Connect to a open network.
+        2. Delete old files under data/vendor/tombstones/wifi
+        3. Call lshal debug on wifi hal component
+        4. Verify that files are created under data/vender/tombstones/wifi
+        """
+        wutils.connect_to_wifi_network(self.dut, self.open_network)
+        time.sleep(DEFAULT_WAIT_TIME)
+        self.dut.adb.shell("rm data/vendor/tombstones/wifi/*")
+        try:
+            self.dut.adb.shell("lshal debug android.hardware.wifi@1.2::IWifi")
+        except UnicodeDecodeError:
+            """ Gets this error because adb.shell trys to parse the output to a string
+            but ringbuffer dumps should already be generated """
+            self.log.info("Unicode decode error occurred, but this is ok")
+        file_count_plus_one = self.dut.adb.shell("ls -l data/vendor/tombstones/wifi | wc -l")
+        if int(file_count_plus_one) <= 1:
+            raise signals.TestFailure("Failed to create ringbuffer debug files.")
\ No newline at end of file
diff --git a/acts/tests/google/wifi/WifiIOTTest.py b/acts/tests/google/wifi/WifiIOTTest.py
index 6e68d70..ec0a314 100755
--- a/acts/tests/google/wifi/WifiIOTTest.py
+++ b/acts/tests/google/wifi/WifiIOTTest.py
@@ -37,7 +37,6 @@
     """
 
     def __init__(self, controllers):
-        self.attenuators = None
         WifiBaseTest.__init__(self, controllers)
 
     def setup_class(self):
diff --git a/acts/tests/google/wifi/WifiManagerTest.py b/acts/tests/google/wifi/WifiManagerTest.py
index 41f65c4..5a5dfeb 100755
--- a/acts/tests/google/wifi/WifiManagerTest.py
+++ b/acts/tests/google/wifi/WifiManagerTest.py
@@ -494,12 +494,15 @@
     """Tests"""
 
     @test_tracker_info(uuid="525fc5e3-afba-4bfd-9a02-5834119e3c66")
-    def test_toggle_state(self):
+    def test_toggle_wifi_state_and_get_startupTime(self):
         """Test toggling wifi"""
         self.log.debug("Going from on to off.")
         wutils.wifi_toggle_state(self.dut, False)
         self.log.debug("Going from off to on.")
+        startTime = time.time()
         wutils.wifi_toggle_state(self.dut, True)
+        startup_time = time.time() - startTime
+        self.log.debug("WiFi was enabled on the device in %s s." % startup_time)
 
     @test_tracker_info(uuid="e9d11563-2bbe-4c96-87eb-ec919b51435b")
     def test_toggle_with_screen(self):
diff --git a/acts/tests/google/wifi/WifiPnoTest.py b/acts/tests/google/wifi/WifiPnoTest.py
index b8f85c0..1282929 100644
--- a/acts/tests/google/wifi/WifiPnoTest.py
+++ b/acts/tests/google/wifi/WifiPnoTest.py
@@ -144,7 +144,7 @@
     """ Tests Begin """
 
     @test_tracker_info(uuid="33d3cae4-5fa7-4e90-b9e2-5d3747bba64c")
-    def test_simple_pno_connection_2g_to_5g(self):
+    def test_simple_pno_connection_to_2g(self):
         """Test PNO triggered autoconnect to a network.
 
         Steps:
@@ -152,16 +152,13 @@
         2. Save 2 valid network configurations (a & b) in the device.
         3. Attenuate 5Ghz network and wait for a few seconds to trigger PNO.
         4. Check the device connected to 2Ghz network automatically.
-        5. Attenuate 2Ghz network and wait for a few seconds to trigger PNO.
-        6. Check the device connected to 5Ghz network automatically.
         """
         self.add_network_and_enable(self.pno_network_a)
         self.add_network_and_enable(self.pno_network_b)
         self.trigger_pno_and_assert_connect("a_on_b_off", self.pno_network_a)
-        self.trigger_pno_and_assert_connect("b_on_a_off", self.pno_network_b)
 
     @test_tracker_info(uuid="39b945a1-830f-4f11-9e6a-9e9641066a96")
-    def test_simple_pno_connection_5g_to_2g(self):
+    def test_simple_pno_connection_to_5g(self):
         """Test PNO triggered autoconnect to a network.
 
         Steps:
@@ -169,15 +166,11 @@
         2. Save 2 valid network configurations (a & b) in the device.
         3. Attenuate 2Ghz network and wait for a few seconds to trigger PNO.
         4. Check the device connected to 5Ghz network automatically.
-        5. Attenuate 5Ghz network and wait for a few seconds to trigger PNO.
-        6. Check the device connected to 2Ghz network automatically.
 
         """
         self.add_network_and_enable(self.pno_network_a)
         self.add_network_and_enable(self.pno_network_b)
         self.trigger_pno_and_assert_connect("b_on_a_off", self.pno_network_b)
-        self.trigger_pno_and_assert_connect("a_on_b_off", self.pno_network_a)
-
 
     @test_tracker_info(uuid="844b15be-ff45-4b09-a11b-0b2b4bb13b22")
     def test_pno_connection_with_multiple_saved_networks(self):
@@ -195,7 +188,8 @@
         self.add_and_enable_dummy_networks(16)
         self.add_network_and_enable(self.pno_network_a)
         self.add_network_and_enable(self.pno_network_b)
+        # Force single scan so that both networks become preferred before PNO.
+        wutils.start_wifi_connection_scan(self.dut)
         self.trigger_pno_and_assert_connect("a_on_b_off", self.pno_network_a)
-        self.trigger_pno_and_assert_connect("b_on_a_off", self.pno_network_b)
 
     """ Tests End """
diff --git a/acts/tests/google/wifi/WifiScannerMultiScanTest.py b/acts/tests/google/wifi/WifiScannerMultiScanTest.py
index 0ff3574..1b33e57 100755
--- a/acts/tests/google/wifi/WifiScannerMultiScanTest.py
+++ b/acts/tests/google/wifi/WifiScannerMultiScanTest.py
@@ -149,15 +149,12 @@
                                            'numUsage': 0,
                                            'SSID': '"wh_ap1_2g"',
                                            'timestamp': 4280078660,
-                                           'numConnection': 0,
                                            'BSSID': '30:b5:c2:33:f9:05',
                                            'frequency': 2412,
-                                           'numIpConfigFailures': 0,
                                            'distanceSdCm': 0,
                                            'distanceCm': 0,
                                            'centerFreq1': 0,
                                            'centerFreq0': 0,
-                                           'blackListTimestamp': 0,
                                            'venueName': '',
                                            'seen': 0,
                                            'operatorFriendlyName': '',
diff --git a/acts/tests/google/wifi/WifiScannerScanTest.py b/acts/tests/google/wifi/WifiScannerScanTest.py
index 9eb6d38..b0d73de 100755
--- a/acts/tests/google/wifi/WifiScannerScanTest.py
+++ b/acts/tests/google/wifi/WifiScannerScanTest.py
@@ -75,12 +75,15 @@
             "test_single_scan_while_pno",
             "test_wifi_connection_and_pno_while_batch_scan",
             "test_wifi_scanner_single_scan_in_isolated",
-            "test_wifi_scanner_with_invalid_numBssidsPerScan")
+            "test_wifi_scanner_with_invalid_numBssidsPerScan",
+            "test_wifi_scanner_dual_radio_low_latency",
+            "test_wifi_scanner_dual_radio_low_power",
+            "test_wifi_scanner_dual_radio_high_accuracy")
 
     def setup_class(self):
         self.dut = self.android_devices[0]
         wutils.wifi_test_device_init(self.dut)
-        req_params = ("run_extended_test", "ping_addr", "max_bugreports")
+        req_params = ("run_extended_test", "ping_addr", "max_bugreports", "dbs_supported_models")
         opt_param = ["reference_networks"]
         self.unpack_userparams(
             req_param_names=req_params, opt_param_names=opt_param)
@@ -108,6 +111,7 @@
         self.attenuators = wutils.group_attenuators(self.attenuators)
         self.attenuators[0].set_atten(0)
         self.attenuators[1].set_atten(0)
+        self.dut.droid.wifiEnableWifiConnectivityManager(False)
 
     def teardown_test(self):
         base_test.BaseTestClass.teardown_test(self)
@@ -121,6 +125,7 @@
         self.dut.cat_adb_log(test_name, begin_time)
 
     def teardown_class(self):
+        self.dut.droid.wifiEnableWifiConnectivityManager(True)
         if "AccessPoint" in self.user_params:
             del self.user_params["reference_networks"]
             del self.user_params["open_network"]
@@ -317,6 +322,8 @@
            parameter.
         3. Pop all full scan result events occurred earlier.
         4. Verify that full scan results match with normal scan results.
+        5. If the scan type is included in scan_setting, verify that the
+           radioChainInfos length.
 
         Args:
             scan_setting: The parameters for the single scan.
@@ -348,12 +355,27 @@
             asserts.assert_true(
                 len(results) >= bssids,
                 "Full single shot result don't match {}".format(len(results)))
+            if 'type' in scan_setting.keys():
+                for item in results:
+                    self.verify_radio_chain_length(scan_setting['type'], item)
         except queue.Empty as error:
             raise AssertionError(
                 "Event did not triggered for single shot {}".format(error))
         finally:
             self.dut.droid.wifiScannerStopScan(idx)
 
+    def verify_radio_chain_length(self, scan_setting_type, scan_result):
+        llen = len(scan_result[0]["radioChainInfos"])
+        if scan_setting_type == wutils.WifiEnums.SCAN_TYPE_LOW_LATENCY \
+            or scan_setting_type == wutils.WifiEnums.SCAN_TYPE_LOW_POWER:
+            asserts.assert_true(llen == 1,
+                                "radioChainInfos len expected:{} "
+                                "actual:{}".format(1, llen))
+        else:
+            asserts.assert_true(llen == 2,
+                                "radioChainInfos len expected:{} "
+                                "actual:{}".format(2, llen))
+
     def wifi_scanner_batch_scan_full(self, scan_setting):
         """Common logic for batch scan test case for full scan result.
 
@@ -953,6 +975,63 @@
                         wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN}
         self.wifi_scanner_single_scan(scan_setting)
 
+    @test_tracker_info(uuid="7c8da0c4-dec7-4d04-abd4-f8ea467a5c6d")
+    def test_wifi_scanner_dual_radio_low_latency(self):
+        """Test WiFi scanner single scan for mix channel with default setting
+           parameters.
+
+         1. Start WifiScanner single scan for type = SCAN_TYPE_LOW_LATENCY.
+         2. Verify that scan results match with respective scan settings.
+        """
+        if self.dut.model not in self.dbs_supported_models:
+            asserts.skip(
+                ("Device %s does not support dual radio scanning.")
+                % self.dut.model)
+        scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+                        "periodInMs": SCANTIME,
+                        "reportEvents":
+                            wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+                        "type": wutils.WifiEnums.SCAN_TYPE_LOW_LATENCY}
+        self.wifi_scanner_single_scan_full(scan_setting)
+
+    @test_tracker_info(uuid="58b49b01-851b-4e45-b218-9fd27c0be921")
+    def test_wifi_scanner_dual_radio_low_power(self):
+        """Test WiFi scanner single scan for mix channel with default setting
+           parameters.
+
+         1. Start WifiScanner single scan for type = SCAN_TYPE_LOW_POWER.
+         2. Verify that scan results match with respective scan settings.
+        """
+        if self.dut.model not in self.dbs_supported_models:
+            asserts.skip(
+                ("Device %s does not support dual radio scanning.")
+                % self.dut.model)
+        scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+                        "periodInMs": SCANTIME,
+                        "reportEvents":
+                            wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+                        "type": wutils.WifiEnums.SCAN_TYPE_LOW_POWER}
+        self.wifi_scanner_single_scan_full(scan_setting)
+
+    @test_tracker_info(uuid="3e7288bc-45e4-497c-bf3a-977eec4e896e")
+    def test_wifi_scanner_dual_radio_high_accuracy(self):
+        """Test WiFi scanner single scan for mix channel with default setting
+           parameters.
+
+         1. Start WifiScanner single scan for type = SCAN_TYPE_HIGH_ACCURACY.
+         2. Verify that scan results match with respective scan settings.
+        """
+        if self.dut.model not in self.dbs_supported_models:
+            asserts.skip(
+                ("Device %s does not support dual radio scanning.")
+                % self.dut.model)
+        scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+                        "periodInMs": SCANTIME,
+                        "reportEvents":
+                            wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+                        "type": wutils.WifiEnums.SCAN_TYPE_HIGH_ACCURACY}
+        self.wifi_scanner_single_scan_full(scan_setting)
+
     @test_tracker_info(uuid="e9f3aaad-4af3-4c54-9829-65dc1d6d4987")
     def test_wifi_scanner_batch_scan_channel_sanity(self):
         """Test WiFi scanner batch scan for mix channel with default setting
diff --git a/acts/tests/google/wifi/WifiSoftApTest.py b/acts/tests/google/wifi/WifiSoftApTest.py
index 987b14d..0d722e3 100644
--- a/acts/tests/google/wifi/WifiSoftApTest.py
+++ b/acts/tests/google/wifi/WifiSoftApTest.py
@@ -55,7 +55,6 @@
         utils.require_sl4a((self.dut, self.dut_client))
         utils.sync_device_time(self.dut)
         utils.sync_device_time(self.dut_client)
-
         # Set country code explicitly to "US".
         self.dut.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
         self.dut_client.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
diff --git a/acts/tests/google/wifi/WifiStressTest.py b/acts/tests/google/wifi/WifiStressTest.py
index 01d9a6f..aee3d3b 100755
--- a/acts/tests/google/wifi/WifiStressTest.py
+++ b/acts/tests/google/wifi/WifiStressTest.py
@@ -32,7 +32,7 @@
 WAIT_BEFORE_CONNECTION = 30
 
 TIMEOUT = 1
-
+PING_ADDR = 'www.google.com'
 
 class WifiStressTest(WifiBaseTest):
     """WiFi Stress test class.
@@ -80,11 +80,11 @@
     def teardown_test(self):
         self.dut.droid.wakeLockRelease()
         self.dut.droid.goToSleepNow()
+        wutils.reset_wifi(self.dut)
 
     def on_fail(self, test_name, begin_time):
         self.dut.take_bug_report(test_name, begin_time)
         self.dut.cat_adb_log(test_name, begin_time)
-        pass
 
     def teardown_class(self):
         wutils.reset_wifi(self.dut)
@@ -120,6 +120,19 @@
             ssid)
         wutils.wifi_connect_by_id(self.dut, net_id)
 
+    def run_ping(self, sec):
+        """Run ping for given number of seconds.
+
+        Args:
+            sec: Time in seconds to run teh ping traffic.
+
+        """
+        self.log.info("Running ping for %d seconds" % sec)
+        result = self.dut.adb.shell("ping -w %d %s" %(sec, PING_ADDR),
+            timeout=sec+1)
+        self.log.debug("Ping Result = %s" % result)
+        if "100% packet loss" in result:
+            raise signals.TestFailure("100% packet loss during ping")
 
     """Tests"""
 
@@ -128,13 +141,20 @@
         """Toggle WiFi state ON and OFF for N times."""
         for count in range(self.stress_count):
             """Test toggling wifi"""
-            self.log.debug("Going from on to off.")
-            wutils.wifi_toggle_state(self.dut, False)
-            self.log.debug("Going from off to on.")
-            startTime = time.time()
-            wutils.wifi_toggle_state(self.dut, True)
-            startup_time = time.time() - startTime
-            self.log.debug("WiFi was enabled on the device in %s s." % startup_time)
+            try:
+                self.log.debug("Going from on to off.")
+                wutils.wifi_toggle_state(self.dut, False)
+                self.log.debug("Going from off to on.")
+                startTime = time.time()
+                wutils.wifi_toggle_state(self.dut, True)
+                startup_time = time.time() - startTime
+                self.log.debug("WiFi was enabled on the device in %s s." %
+                    startup_time)
+            except:
+                signals.TestFailure(details="", extras={"Iterations":"%d" %
+                    self.stress_count, "Pass":"%d" %count})
+        raise signals.TestPass(details="", extras={"Iterations":"%d" %
+            self.stress_count, "Pass":"%d" %(count+1)})
 
     @test_tracker_info(uuid="49e3916a-9580-4bf7-a60d-a0f2545dcdde")
     def test_stress_connect_traffic_disconnect_5g(self):
@@ -148,21 +168,26 @@
 
         """
         for count in range(self.stress_count):
-            net_id = self.dut.droid.wifiAddNetwork(self.wpa_5g)
-            asserts.assert_true(net_id != -1, "Add network %r failed" % self.wpa_5g)
-            self.dut.droid.wifiEnableNetwork(net_id, 0)
-            self.scan_and_connect_by_id(self.wpa_5g, net_id)
-            # Start IPerf traffic from phone to server.
-            # Upload data for 10s.
-            args = "-p {} -t {}".format(self.iperf_server.port, 10)
-            self.log.info("Running iperf client {}".format(args))
-            result, data = self.dut.run_iperf_client(self.iperf_server_address, args)
-            if not result:
-                self.log.debug("Error occurred in iPerf traffic.")
-                raise signals.TestFailure("Error occurred in iPerf traffic. Current"
-                    " WiFi state = %d" % self.dut.droid.wifiCheckState())
-            wutils.wifi_forget_network(self.dut,self.wpa_5g[WifiEnums.SSID_KEY])
-            time.sleep(WAIT_BEFORE_CONNECTION)
+            try:
+                net_id = self.dut.droid.wifiAddNetwork(self.wpa_5g)
+                asserts.assert_true(net_id != -1, "Add network %r failed" % self.wpa_5g)
+                self.scan_and_connect_by_id(self.wpa_5g, net_id)
+                # Start IPerf traffic from phone to server.
+                # Upload data for 10s.
+                args = "-p {} -t {}".format(self.iperf_server.port, 10)
+                self.log.info("Running iperf client {}".format(args))
+                result, data = self.dut.run_iperf_client(self.iperf_server_address, args)
+                if not result:
+                    self.log.debug("Error occurred in iPerf traffic.")
+                    self.run_ping(10)
+                wutils.wifi_forget_network(self.dut,self.wpa_5g[WifiEnums.SSID_KEY])
+                time.sleep(WAIT_BEFORE_CONNECTION)
+            except:
+                raise signals.TestFailure("Network connect-disconnect failed."
+                    "Look at logs", extras={"Iterations":"%d" %
+                        self.stress_count, "Pass":"%d" %count})
+        raise signals.TestPass(details="", extras={"Iterations":"%d" %
+            self.stress_count, "Pass":"%d" %(count+1)})
 
     @test_tracker_info(uuid="e9827dff-0755-43ec-8b50-1f9756958460")
     def test_stress_connect_long_traffic_5g(self):
@@ -174,19 +199,24 @@
                3. Verify no WiFi disconnects/data interruption.
 
         """
-        self.scan_and_connect_by_ssid(self.wpa_5g)
-        # Start IPerf traffic from server to phone.
-        # Download data for 5 hours.
-        sec = self.stress_hours * 60 * 60
-        args = "-p {} -t {} -R".format(self.iperf_server.port, sec)
-        self.log.info("Running iperf client {}".format(args))
-        result, data = self.dut.run_iperf_client(self.iperf_server_address,
-            args, timeout=sec+1)
-        self.dut.droid.wifiDisconnect()
-        if not result:
-            self.log.debug("Error occurred in iPerf traffic.")
-            raise signals.TestFailure("Error occurred in iPerf traffic. Current"
-                " WiFi state = %d" % self.dut.droid.wifiCheckState())
+        try:
+            self.scan_and_connect_by_ssid(self.wpa_5g)
+            # Start IPerf traffic from server to phone.
+            # Download data for 5 hours.
+            sec = self.stress_hours * 60 * 60
+            args = "-p {} -t {} -R".format(self.iperf_server.port, sec)
+            self.log.info("Running iperf client {}".format(args))
+            result, data = self.dut.run_iperf_client(self.iperf_server_address,
+                args, timeout=sec+1)
+            if not result:
+                self.log.debug("Error occurred in iPerf traffic.")
+                self.run_ping(sec)
+        except:
+            raise signals.TestFailure("Network long-connect failed."
+                "Look at logs", extras={"Total Hours":"%d" %self.stress_hours,
+                    "Seconds Run":"UNKNOWN"})
+        raise signals.TestPass(details="", extras={"Total Hours":"%d" %
+            self.stress_hours, "Seconds":"%d" %sec})
 
     @test_tracker_info(uuid="d367c83e-5b00-4028-9ed8-f7b875997d13")
     def test_stress_wifi_failover(self):
@@ -211,24 +241,28 @@
             time.sleep(WAIT_FOR_AUTO_CONNECT)
             cur_network = self.dut.droid.wifiGetConnectionInfo()
             cur_ssid = cur_network[WifiEnums.SSID_KEY]
-            self.log.debug("Cur_ssid = %s" % cur_ssid)
-            for count in range(0,len(self.networks)):
+            self.log.info("Cur_ssid = %s" % cur_ssid)
+            for i in range(0,len(self.networks)):
                 self.log.debug("Forget network %s" % cur_ssid)
                 wutils.wifi_forget_network(self.dut, cur_ssid)
                 time.sleep(WAIT_FOR_AUTO_CONNECT)
                 cur_network = self.dut.droid.wifiGetConnectionInfo()
                 cur_ssid = cur_network[WifiEnums.SSID_KEY]
-                self.log.debug("Cur_ssid = %s" % cur_ssid)
-                if count == len(self.networks) - 1:
+                self.log.info("Cur_ssid = %s" % cur_ssid)
+                if i == len(self.networks) - 1:
                     break
                 if cur_ssid not in ssids:
                     raise signals.TestFailure("Device did not failover to the "
                         "expected network. SSID = %s" % cur_ssid)
             network_config = self.dut.droid.wifiGetConfiguredNetworks()
-            self.log.debug("Network Config = %s" % network_config)
+            self.log.info("Network Config = %s" % network_config)
             if len(network_config):
                 raise signals.TestFailure("All the network configurations were not "
-                        "removed. Configured networks = %s" % network_config)
+                    "removed. Configured networks = %s" % network_config,
+                        extras={"Iterations":"%d" % self.stress_count,
+                            "Pass":"%d" %(count*4)})
+        raise signals.TestPass(details="", extras={"Iterations":"%d" %
+            self.stress_count, "Pass":"%d" %((count+1)*4)})
 
     @test_tracker_info(uuid="2c19e8d1-ac16-4d7e-b309-795144e6b956")
     def test_stress_softAP_startup_and_stop_5g(self):
@@ -241,14 +275,14 @@
             4. Verify softAP is turned down and WiFi is up.
 
         """
+        # Set country code explicitly to "US".
+        self.dut.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
+        self.dut_client.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
         ap_ssid = "softap_" + utils.rand_ascii_str(8)
         ap_password = utils.rand_ascii_str(8)
         self.dut.log.info("softap setup: %s %s", ap_ssid, ap_password)
         config = {wutils.WifiEnums.SSID_KEY: ap_ssid}
         config[wutils.WifiEnums.PWD_KEY] = ap_password
-        # Set country code explicitly to "US".
-        self.dut.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
-        self.dut_client.droid.wifiSetCountryCode(wutils.WifiEnums.CountryCode.US)
         for count in range(self.stress_count):
             initial_wifi_state = self.dut.droid.wifiCheckState()
             wutils.start_wifi_tethering(self.dut,
@@ -257,31 +291,40 @@
                 WifiEnums.WIFI_CONFIG_APBAND_5G)
             wutils.start_wifi_connection_scan_and_ensure_network_found(
                 self.dut_client, ap_ssid)
-            # Toggle WiFi ON, which inturn calls softAP teardown.
-            wutils.wifi_toggle_state(self.dut, True)
-            time.sleep(TIMEOUT)
+            wutils.stop_wifi_tethering(self.dut)
             asserts.assert_false(self.dut.droid.wifiIsApEnabled(),
                                  "SoftAp failed to shutdown!")
             time.sleep(TIMEOUT)
             cur_wifi_state = self.dut.droid.wifiCheckState()
             if initial_wifi_state != cur_wifi_state:
-                raise signals.TestFailure("Wifi state was %d before softAP and %d now!" %
-                    (initial_wifi_state, cur_wifi_state))
+               raise signals.TestFailure("Wifi state was %d before softAP and %d now!" %
+                    (initial_wifi_state, cur_wifi_state),
+                        extras={"Iterations":"%d" % self.stres_count,
+                            "Pass":"%d" %count})
+        raise signals.TestPass(details="", extras={"Iterations":"%d" %
+            self.stress_count, "Pass":"%d" %(count+1)})
 
     @test_tracker_info(uuid="eb22e26b-95d1-4580-8c76-85dfe6a42a0f")
     def test_stress_wifi_roaming(self):
         AP1_network = self.reference_networks[0]["5g"]
         AP2_network = self.reference_networks[1]["5g"]
         wutils.set_attns(self.attenuators, "AP1_on_AP2_off")
-        wutils.wifi_connect(self.dut, AP1_network)
+        self.scan_and_connect_by_ssid(AP1_network)
         # Reduce iteration to half because each iteration does two roams.
-        for count in range(self.stress_count/2):
+        for count in range(int(self.stress_count/2)):
             self.log.info("Roaming iteration %d, from %s to %s", count,
                            AP1_network, AP2_network)
-            wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
-                "AP1_off_AP2_on", AP2_network)
-            self.log.info("Roaming iteration %d, from %s to %s", count,
-                           AP2_network, AP1_network)
-            wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
-                "AP1_on_AP2_off", AP1_network)
+            try:
+                wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
+                    "AP1_off_AP2_on", AP2_network)
+                self.log.info("Roaming iteration %d, from %s to %s", count,
+                               AP2_network, AP1_network)
+                wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
+                    "AP1_on_AP2_off", AP1_network)
+            except:
+                raise signals.TestFailure("Roaming failed. Look at logs",
+                    extras={"Iterations":"%d" %self.stress_count, "Pass":"%d" %
+                        (count*2)})
+        raise signals.TestPass(details="", extras={"Iterations":"%d" %
+            self.stress_count, "Pass":"%d" %((count+1)*2)})
 
diff --git a/acts/tests/google/wifi/WifiTeleCoexTest.py b/acts/tests/google/wifi/WifiTeleCoexTest.py
index f5d4c7f..3d30640 100644
--- a/acts/tests/google/wifi/WifiTeleCoexTest.py
+++ b/acts/tests/google/wifi/WifiTeleCoexTest.py
@@ -127,9 +127,11 @@
 
         self.log.debug("Toggling wifi ON")
         wifi_utils.wifi_toggle_state(self.dut, True)
+        # Sleep for 1s before getting new WiFi state.
+        time.sleep(1)
         if not self.dut.droid.wifiGetisWifiEnabled():
             raise signals.TestFailure("WiFi did not turn on after turning ON"
-                                      "Airplane mode")
+                                      " Airplane mode")
         asserts.assert_true(
             acts.utils.force_airplane_mode(self.dut, False),
             "Can not turn on airplane mode on: %s" % self.dut.serial)
@@ -159,6 +161,8 @@
             3. Make a short sequence voice call between Phone A and B.
 
         """
+        # Sleep for 5s before getting new WiFi state.
+        time.sleep(5)
         wifi_info = self.dut.droid.wifiGetConnectionInfo()
         if wifi_info[WifiEnums.SSID_KEY] != self.wifi_network_ssid:
             raise signals.TestFailure("Phone failed to connect to %s network on"
diff --git a/acts/tests/google/wifi/aware/functional/AttachTest.py b/acts/tests/google/wifi/aware/functional/AttachTest.py
index 598cca6..37f07e0 100644
--- a/acts/tests/google/wifi/aware/functional/AttachTest.py
+++ b/acts/tests/google/wifi/aware/functional/AttachTest.py
@@ -16,12 +16,13 @@
 
 import time
 
+from acts import asserts
+from acts import utils
 from acts.test_decorators import test_tracker_info
 from acts.test_utils.wifi import wifi_test_utils as wutils
 from acts.test_utils.wifi.aware import aware_const as aconsts
 from acts.test_utils.wifi.aware import aware_test_utils as autils
 from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
-from acts.utils import force_airplane_mode
 
 
 class AttachTest(AwareBaseTest):
@@ -99,8 +100,8 @@
     """Function test case / Attach test cases / attempt to attach with wifi off
 
     Validates that if trying to attach with Wi-Fi disabled will receive the
-    expected failure callback. As a side-effect also validates that the broadcast
-    for Aware unavailable is received.
+    expected failure callback. As a side-effect also validates that the
+    broadcast for Aware unavailable is received.
     """
     dut = self.android_devices[0]
     wutils.wifi_toggle_state(dut, False)
@@ -108,6 +109,39 @@
     dut.droid.wifiAwareAttach()
     autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
 
+  @test_tracker_info(uuid="7dcc4530-c936-4447-9d22-a7c5b315e2ce")
+  def test_attach_with_doze(self):
+    """Function test case / Attach test cases / attempt to attach with doze on
+
+    Validates that if trying to attach with device in doze mode will receive the
+    expected failure callback. As a side-effect also validates that the
+    broadcast for Aware unavailable is received.
+    """
+    dut = self.android_devices[0]
+    asserts.assert_true(utils.enable_doze(dut), "Can't enable doze")
+    autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+    dut.droid.wifiAwareAttach()
+    autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
+    asserts.assert_true(utils.disable_doze(dut), "Can't disable doze")
+    autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+
+  @test_tracker_info(uuid="2574fd01-8974-4dd0-aeb8-a7194461140e")
+  def test_attach_with_location_off(self):
+    """Function test case / Attach test cases / attempt to attach with location
+    mode off.
+
+    Validates that if trying to attach with device location mode off will
+    receive the expected failure callback. As a side-effect also validates that
+    the broadcast for Aware unavailable is received.
+    """
+    dut = self.android_devices[0]
+    utils.set_location_service(dut, False)
+    autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+    dut.droid.wifiAwareAttach()
+    autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
+    utils.set_location_service(dut, True)
+    autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+
   @test_tracker_info(uuid="7ffde8e7-a010-4b77-97f5-959f263b5249")
   def test_attach_apm_toggle_attach_again(self):
     """Validates that enabling Airplane mode while Aware is on resets it
@@ -120,12 +154,12 @@
     autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
 
     # enable airplane mode
-    force_airplane_mode(dut, True)
+    utils.force_airplane_mode(dut, True)
     autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
 
     # wait a few seconds and disable airplane mode
     time.sleep(10)
-    force_airplane_mode(dut, False)
+    utils.force_airplane_mode(dut, False)
     autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
 
     # try enabling Aware again (attach)
diff --git a/acts/tests/google/wifi/aware/functional/DataPathTest.py b/acts/tests/google/wifi/aware/functional/DataPathTest.py
index 66ec715..8afb2a4 100644
--- a/acts/tests/google/wifi/aware/functional/DataPathTest.py
+++ b/acts/tests/google/wifi/aware/functional/DataPathTest.py
@@ -19,6 +19,7 @@
 from acts import asserts
 from acts.test_decorators import test_tracker_info
 from acts.test_utils.net import connectivity_const as cconsts
+from acts.test_utils.wifi import wifi_test_utils as wutils
 from acts.test_utils.wifi.aware import aware_const as aconsts
 from acts.test_utils.wifi.aware import aware_test_utils as autils
 from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
@@ -148,7 +149,8 @@
       use_peer_id,
       passphrase_to_use=None,
       pub_on_both=False,
-      pub_on_both_same=True):
+      pub_on_both_same=True,
+      expect_failure=False):
     """Runs the in-band data-path tests.
 
     Args:
@@ -163,6 +165,8 @@
                    publisher isn't used (existing to test use-case).
       pub_on_both_same: If True then the second publish uses an identical
                         service name, otherwise a different service name.
+      expect_failure: If True then don't expect NDP formation, otherwise expect
+                      NDP setup to succeed.
     """
     (p_dut, s_dut, p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
      peer_id_on_pub) = self.set_up_discovery(ptype, stype, use_peer_id,
@@ -189,51 +193,59 @@
         s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id, peer_id_on_sub,
                                                     passphrase, pmk))
 
-    # Publisher & Subscriber: wait for network formation
-    p_net_event = autils.wait_for_event_with_keys(
-        p_dut, cconsts.EVENT_NETWORK_CALLBACK,
-        autils.EVENT_NDP_TIMEOUT,
-        (cconsts.NETWORK_CB_KEY_EVENT,
-         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
-        (cconsts.NETWORK_CB_KEY_ID, p_req_key))
-    s_net_event = autils.wait_for_event_with_keys(
-        s_dut, cconsts.EVENT_NETWORK_CALLBACK,
-        autils.EVENT_NDP_TIMEOUT,
-        (cconsts.NETWORK_CB_KEY_EVENT,
-         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
-        (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+    if expect_failure:
+      # Publisher & Subscriber: fail on network formation
+      time.sleep(autils.EVENT_NDP_TIMEOUT)
+      autils.fail_on_event_with_keys(p_dut, cconsts.EVENT_NETWORK_CALLBACK, 0,
+                                     (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+      autils.fail_on_event_with_keys(s_dut, cconsts.EVENT_NETWORK_CALLBACK, 0,
+                                     (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+    else:
+      # Publisher & Subscriber: wait for network formation
+      p_net_event = autils.wait_for_event_with_keys(
+          p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+          autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+          (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+      s_net_event = autils.wait_for_event_with_keys(
+          s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+          autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+          (cconsts.NETWORK_CB_KEY_ID, s_req_key))
 
-    p_aware_if = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
-    s_aware_if = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
-    self.log.info("Interface names: p=%s, s=%s", p_aware_if, s_aware_if)
+      p_aware_if = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+      s_aware_if = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+      self.log.info("Interface names: p=%s, s=%s", p_aware_if, s_aware_if)
 
-    p_ipv6 = p_dut.droid.connectivityGetLinkLocalIpv6Address(p_aware_if).split(
-        "%")[0]
-    s_ipv6 = s_dut.droid.connectivityGetLinkLocalIpv6Address(s_aware_if).split(
-        "%")[0]
-    self.log.info("Interface addresses (IPv6): p=%s, s=%s", p_ipv6, s_ipv6)
+      p_ipv6 = \
+      p_dut.droid.connectivityGetLinkLocalIpv6Address(p_aware_if).split("%")[0]
+      s_ipv6 = \
+      s_dut.droid.connectivityGetLinkLocalIpv6Address(s_aware_if).split("%")[0]
+      self.log.info("Interface addresses (IPv6): p=%s, s=%s", p_ipv6, s_ipv6)
 
-    # TODO: possibly send messages back and forth, prefer to use netcat/nc
+      # TODO: possibly send messages back and forth, prefer to use netcat/nc
 
-    # terminate sessions and wait for ON_LOST callbacks
-    p_dut.droid.wifiAwareDestroy(p_id)
-    s_dut.droid.wifiAwareDestroy(s_id)
+      # terminate sessions and wait for ON_LOST callbacks
+      p_dut.droid.wifiAwareDestroy(p_id)
+      s_dut.droid.wifiAwareDestroy(s_id)
 
-    autils.wait_for_event_with_keys(
-        p_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
-        (cconsts.NETWORK_CB_KEY_EVENT,
-         cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, p_req_key))
-    autils.wait_for_event_with_keys(
-        s_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
-        (cconsts.NETWORK_CB_KEY_EVENT,
-         cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+      autils.wait_for_event_with_keys(
+          p_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+      autils.wait_for_event_with_keys(
+          s_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, s_req_key))
 
     # clean-up
     p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
     s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
 
   def run_oob_data_path_test(self, encr_type, use_peer_id,
-      setup_discovery_sessions=False):
+      setup_discovery_sessions=False, expect_failure=False):
     """Runs the out-of-band data-path tests.
 
     Args:
@@ -243,6 +255,8 @@
       setup_discovery_sessions: If True also set up a (spurious) discovery
         session (pub on both sides, sub on Responder side). Validates a corner
         case.
+      expect_failure: If True then don't expect NDP formation, otherwise expect
+                      NDP setup to succeed.
     """
     init_dut = self.android_devices[0]
     init_dut.pretty_name = "Initiator"
@@ -299,47 +313,57 @@
         init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
             init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, passphrase, pmk))
 
-    # Initiator & Responder: wait for network formation
-    init_net_event = autils.wait_for_event_with_keys(
-        init_dut, cconsts.EVENT_NETWORK_CALLBACK,
-        autils.EVENT_NDP_TIMEOUT,
-        (cconsts.NETWORK_CB_KEY_EVENT,
-         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
-        (cconsts.NETWORK_CB_KEY_ID, init_req_key))
-    resp_net_event = autils.wait_for_event_with_keys(
-        resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
-        autils.EVENT_NDP_TIMEOUT,
-        (cconsts.NETWORK_CB_KEY_EVENT,
-         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
-        (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+    if expect_failure:
+      # Initiator & Responder: fail on network formation
+      time.sleep(autils.EVENT_NDP_TIMEOUT)
+      autils.fail_on_event_with_keys(resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+                                     0,
+                                     (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+      autils.fail_on_event_with_keys(init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+                                     0,
+                                     (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+    else:
+      # Initiator & Responder: wait for network formation
+      init_net_event = autils.wait_for_event_with_keys(
+          init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+          autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+          (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+      resp_net_event = autils.wait_for_event_with_keys(
+          resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+          autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+          (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
 
-    init_aware_if = init_net_event["data"][
-      cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
-    resp_aware_if = resp_net_event["data"][
-      cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
-    self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if)
+      init_aware_if = init_net_event["data"][
+        cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+      resp_aware_if = resp_net_event["data"][
+        cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+      self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if)
 
-    init_ipv6 = init_dut.droid.connectivityGetLinkLocalIpv6Address(
-        init_aware_if).split("%")[0]
-    resp_ipv6 = resp_dut.droid.connectivityGetLinkLocalIpv6Address(
-        resp_aware_if).split("%")[0]
-    self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
-                  resp_ipv6)
+      init_ipv6 = init_dut.droid.connectivityGetLinkLocalIpv6Address(
+          init_aware_if).split("%")[0]
+      resp_ipv6 = resp_dut.droid.connectivityGetLinkLocalIpv6Address(
+          resp_aware_if).split("%")[0]
+      self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+                    resp_ipv6)
 
-    # TODO: possibly send messages back and forth, prefer to use netcat/nc
+      # TODO: possibly send messages back and forth, prefer to use netcat/nc
 
-    # terminate sessions and wait for ON_LOST callbacks
-    init_dut.droid.wifiAwareDestroy(init_id)
-    resp_dut.droid.wifiAwareDestroy(resp_id)
+      # terminate sessions and wait for ON_LOST callbacks
+      init_dut.droid.wifiAwareDestroy(init_id)
+      resp_dut.droid.wifiAwareDestroy(resp_id)
 
-    autils.wait_for_event_with_keys(
-        init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
-        (cconsts.NETWORK_CB_KEY_EVENT,
-         cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, init_req_key))
-    autils.wait_for_event_with_keys(
-        resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
-        (cconsts.NETWORK_CB_KEY_EVENT,
-         cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+      autils.wait_for_event_with_keys(
+          init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+      autils.wait_for_event_with_keys(
+          resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LOST), (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
 
     # clean-up
     resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
@@ -638,13 +662,14 @@
   #            or different from the primary session.
   # pub_type: Type of publish discovery session: unsolicited or solicited.
   # sub_type: Type of subscribe discovery session: passive or active.
-  # encr_type: Encription type: open, passphrase
+  # encr_type: Encryption type: open, passphrase
   # peer_spec: Peer specification method: any or specific
   #
   # Note: In-Band means using Wi-Fi Aware for discovery and referring to the
   # peer using the Aware-provided peer handle (as opposed to a MAC address).
   #######################################
 
+  @test_tracker_info(uuid="e855dd81-45c8-4bb2-a204-7687c48ff843")
   def test_ib_extra_pub_same_unsolicited_passive_open_specific(self):
     """Data-path: in-band, unsolicited/passive, open encryption, specific peer.
 
@@ -661,6 +686,7 @@
         pub_on_both=True,
         pub_on_both_same=True)
 
+  @test_tracker_info(uuid="57fc9d53-32ae-470f-a8b1-2fe37893687d")
   def test_ib_extra_pub_same_unsolicited_passive_open_any(self):
     """Data-path: in-band, unsolicited/passive, open encryption, any peer.
 
@@ -677,6 +703,7 @@
         pub_on_both=True,
         pub_on_both_same=True)
 
+  @test_tracker_info(uuid="7a32f439-d745-4716-a75e-b54109aaaf82")
   def test_ib_extra_pub_diff_unsolicited_passive_open_specific(self):
     """Data-path: in-band, unsolicited/passive, open encryption, specific peer.
 
@@ -693,6 +720,7 @@
         pub_on_both=True,
         pub_on_both_same=False)
 
+  @test_tracker_info(uuid="a14ddc66-88fd-4b49-ab37-225533867c63")
   def test_ib_extra_pub_diff_unsolicited_passive_open_any(self):
     """Data-path: in-band, unsolicited/passive, open encryption, any peer.
 
@@ -715,7 +743,7 @@
   # names is: test_oob_<encr_type>_<peer_spec>
   # where:
   #
-  # encr_type: Encription type: open, passphrase
+  # encr_type: Encryption type: open, passphrase
   # peer_spec: Peer specification method: any or specific
   #
   # Optionally set up an extra discovery session to test coexistence. If so
@@ -785,6 +813,7 @@
         encr_type=self.ENCR_TYPE_PMK,
         use_peer_id=False)
 
+  @test_tracker_info(uuid="dd464f24-b404-4eea-955c-d10c9e8adefc")
   def test_oob_ib_coex_open_specific(self):
     """Data-path: out-of-band, open encryption, specific peer - in-band coex:
     set up a concurrent discovery session to verify no impact. The session
@@ -797,6 +826,7 @@
         use_peer_id=True,
         setup_discovery_sessions=True)
 
+  @test_tracker_info(uuid="088fcd3a-b015-4179-a9a5-91f782b03e3b")
   def test_oob_ib_coex_open_any(self):
     """Data-path: out-of-band, open encryption, any peer - in-band coex:
     set up a concurrent discovery session to verify no impact. The session
@@ -928,7 +958,8 @@
     """
     num_events = 0
     while num_events != len(req_keys):
-      event = autils.wait_for_event(dut, cconsts.EVENT_NETWORK_CALLBACK)
+      event = autils.wait_for_event(dut, cconsts.EVENT_NETWORK_CALLBACK,
+                                    timeout=autils.EVENT_NDP_TIMEOUT)
       if (event["data"][cconsts.NETWORK_CB_KEY_EVENT] ==
           cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED):
         if event["data"][cconsts.NETWORK_CB_KEY_ID] in req_keys:
@@ -1204,6 +1235,8 @@
     dut1_req_keys = []
     dut2_aware_ifs = []
     dut1_aware_ifs = []
+    dut2_aware_ipv6 = []
+    dut1_aware_ipv6 = []
 
     dut2_type = aconsts.DATA_PATH_RESPONDER
     dut1_type = aconsts.DATA_PATH_INITIATOR
@@ -1244,20 +1277,24 @@
 
       # Wait for network
       dut1_net_event = autils.wait_for_event_with_keys(
-          dut1, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
+          dut1, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
           (cconsts.NETWORK_CB_KEY_EVENT,
            cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
           (cconsts.NETWORK_CB_KEY_ID, dut1_req_key))
       dut2_net_event = autils.wait_for_event_with_keys(
-          dut2, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
+          dut2, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
           (cconsts.NETWORK_CB_KEY_EVENT,
            cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
           (cconsts.NETWORK_CB_KEY_ID, dut2_req_key))
 
-      dut2_aware_ifs.append(
-          dut2_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
-      dut1_aware_ifs.append(
-          dut1_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
+      dut2_aware_if = dut2_net_event["data"][
+        cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+      dut1_aware_if = dut1_net_event["data"][
+        cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+      dut2_aware_ifs.append(dut2_aware_if)
+      dut1_aware_ifs.append(dut1_aware_if)
+      dut2_aware_ipv6.append(autils.get_ipv6_addr(dut2, dut2_aware_if))
+      dut1_aware_ipv6.append(autils.get_ipv6_addr(dut1, dut1_aware_if))
 
       if flip_init_resp:
         if dut2_is_responder:
@@ -1268,12 +1305,16 @@
           dut1_type = aconsts.DATA_PATH_INITIATOR
         dut2_is_responder = not dut2_is_responder
 
-    # check that we are using 2 NDIs
+    # check that we are using 2 NDIs & that they have unique IPv6 addresses
     dut1_aware_ifs = list(set(dut1_aware_ifs))
     dut2_aware_ifs = list(set(dut2_aware_ifs))
+    dut1_aware_ipv6 = list(set(dut1_aware_ipv6))
+    dut2_aware_ipv6 = list(set(dut2_aware_ipv6))
 
     self.log.info("Interface names: DUT1=%s, DUT2=%s", dut1_aware_ifs,
                   dut2_aware_ifs)
+    self.log.info("IPv6 addresses: DUT1=%s, DUT2=%s", dut1_aware_ipv6,
+                  dut2_aware_ipv6)
     self.log.info("DUT1 requests: %s", dut1_req_keys)
     self.log.info("DUT2 requests: %s", dut2_req_keys)
 
@@ -1281,6 +1322,10 @@
         len(dut1_aware_ifs), len(sec_configs), "Multiple DUT1 interfaces")
     asserts.assert_equal(
         len(dut2_aware_ifs), len(sec_configs), "Multiple DUT2 interfaces")
+    asserts.assert_equal(
+        len(dut1_aware_ipv6), len(sec_configs), "Multiple DUT1 IPv6 addresses")
+    asserts.assert_equal(
+        len(dut2_aware_ipv6), len(sec_configs), "Multiple DUT2 IPv6 addresses")
 
     for i in range(len(sec_configs)):
       if_name = "%s%d" % (aconsts.AWARE_NDI_PREFIX, i)
@@ -1302,41 +1347,42 @@
 
   @test_tracker_info(uuid="2d728163-11cc-46ba-a973-c8e1e71397fc")
   def test_multiple_ndi_open_passphrase(self):
-    """Verify that can between 2 DUTs can create 2 NDPs with different security
+    """Verify that between 2 DUTs can create 2 NDPs with different security
     configuration (one open, one using passphrase). The result should use two
     different NDIs"""
     self.run_multiple_ndi([None, self.PASSPHRASE])
 
   @test_tracker_info(uuid="5f2c32aa-20b2-41f0-8b1e-d0b68df73ada")
   def test_multiple_ndi_open_pmk(self):
-    """Verify that can between 2 DUTs can create 2 NDPs with different security
+    """Verify that between 2 DUTs can create 2 NDPs with different security
     configuration (one open, one using pmk). The result should use two
     different NDIs"""
     self.run_multiple_ndi([None, self.PMK])
 
   @test_tracker_info(uuid="34467659-bcfb-40cd-ba25-7e50560fca63")
   def test_multiple_ndi_passphrase_pmk(self):
-    """Verify that can between 2 DUTs can create 2 NDPs with different security
+    """Verify that between 2 DUTs can create 2 NDPs with different security
     configuration (one using passphrase, one using pmk). The result should use
     two different NDIs"""
     self.run_multiple_ndi([self.PASSPHRASE, self.PMK])
 
   @test_tracker_info(uuid="d9194ce6-45b6-41b1-9cc8-ada79968966d")
   def test_multiple_ndi_passphrases(self):
-    """Verify that can between 2 DUTs can create 2 NDPs with different security
+    """Verify that between 2 DUTs can create 2 NDPs with different security
     configuration (using different passphrases). The result should use two
     different NDIs"""
     self.run_multiple_ndi([self.PASSPHRASE, self.PASSPHRASE2])
 
   @test_tracker_info(uuid="879df795-62d2-40d4-a862-bd46d8f7e67f")
   def test_multiple_ndi_pmks(self):
-    """Verify that can between 2 DUTs can create 2 NDPs with different security
+    """Verify that between 2 DUTs can create 2 NDPs with different security
     configuration (using different PMKS). The result should use two different
     NDIs"""
     self.run_multiple_ndi([self.PMK, self.PMK2])
 
+  @test_tracker_info(uuid="397d380a-8e41-466e-9ccb-cf8f413d83ba")
   def test_multiple_ndi_open_passphrase_flip(self):
-    """Verify that can between 2 DUTs can create 2 NDPs with different security
+    """Verify that between 2 DUTs can create 2 NDPs with different security
     configuration (one open, one using passphrase). The result should use two
     different NDIs.
 
@@ -1344,8 +1390,9 @@
     """
     self.run_multiple_ndi([None, self.PASSPHRASE], flip_init_resp=True)
 
+  @test_tracker_info(uuid="b3a4300b-1514-4cb8-a814-9c2baa449700")
   def test_multiple_ndi_open_pmk_flip(self):
-    """Verify that can between 2 DUTs can create 2 NDPs with different security
+    """Verify that between 2 DUTs can create 2 NDPs with different security
     configuration (one open, one using pmk). The result should use two
     different NDIs
 
@@ -1353,8 +1400,9 @@
     """
     self.run_multiple_ndi([None, self.PMK], flip_init_resp=True)
 
+  @test_tracker_info(uuid="0bfea9e4-e57d-417f-8db4-245741e9bbd5")
   def test_multiple_ndi_passphrase_pmk_flip(self):
-    """Verify that can between 2 DUTs can create 2 NDPs with different security
+    """Verify that between 2 DUTs can create 2 NDPs with different security
     configuration (one using passphrase, one using pmk). The result should use
     two different NDIs
 
@@ -1362,8 +1410,9 @@
     """
     self.run_multiple_ndi([self.PASSPHRASE, self.PMK], flip_init_resp=True)
 
+  @test_tracker_info(uuid="74023483-5417-431b-a362-991ad4a03ab8")
   def test_multiple_ndi_passphrases_flip(self):
-    """Verify that can between 2 DUTs can create 2 NDPs with different security
+    """Verify that between 2 DUTs can create 2 NDPs with different security
     configuration (using different passphrases). The result should use two
     different NDIs
 
@@ -1372,11 +1421,505 @@
     self.run_multiple_ndi([self.PASSPHRASE, self.PASSPHRASE2],
                           flip_init_resp=True)
 
+  @test_tracker_info(uuid="873b2d91-28a1-403f-ae9c-d756bb2f59ee")
   def test_multiple_ndi_pmks_flip(self):
-    """Verify that can between 2 DUTs can create 2 NDPs with different security
+    """Verify that between 2 DUTs can create 2 NDPs with different security
     configuration (using different PMKS). The result should use two different
     NDIs
 
     Flip Initiator and Responder roles.
     """
     self.run_multiple_ndi([self.PMK, self.PMK2], flip_init_resp=True)
+
+  #######################################
+
+  @test_tracker_info(uuid="2f10a9df-7fbd-490d-a238-3523f47ab54c")
+  def test_ib_responder_any_usage(self):
+    """Verify that configuring an in-band (Aware discovery) Responder to receive
+    an NDP request from any peer is not permitted by current API level. Override
+    API check to validate that possible (i.e. that failure at current API level
+    is due to an API check and not some underlying failure).
+    """
+
+    # configure all devices to override API check and allow a Responder from ANY
+    for ad in self.android_devices:
+      autils.configure_ndp_allow_any_override(ad, True)
+    self.run_ib_data_path_test(
+        ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+        stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+        encr_type=self.ENCR_TYPE_OPEN,
+        use_peer_id=False)
+
+    # configure all devices to respect API check - i.e. disallow a Responder
+    # from ANY
+    for ad in self.android_devices:
+      autils.configure_ndp_allow_any_override(ad, False)
+    self.run_ib_data_path_test(
+        ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+        stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+        encr_type=self.ENCR_TYPE_OPEN,
+        use_peer_id=False,
+        expect_failure=True)
+
+  @test_tracker_info(uuid="5889cd41-0a72-4b7b-ab82-5b9168b9b5b8")
+  def test_oob_responder_any_usage(self):
+    """Verify that configuring an out-of-band (Aware discovery) Responder to
+    receive an NDP request from any peer is not permitted by current API level.
+    Override API check to validate that possible (i.e. that failure at current
+    API level is due to an API check and not some underlying failure).
+    """
+
+    # configure all devices to override API check and allow a Responder from ANY
+    for ad in self.android_devices:
+      autils.configure_ndp_allow_any_override(ad, True)
+    self.run_oob_data_path_test(
+        encr_type=self.ENCR_TYPE_OPEN,
+        use_peer_id=False)
+
+    # configure all devices to respect API check - i.e. disallow a Responder
+    # from ANY
+    for ad in self.android_devices:
+      autils.configure_ndp_allow_any_override(ad, False)
+    self.run_oob_data_path_test(
+        encr_type=self.ENCR_TYPE_OPEN,
+        use_peer_id=False,
+        expect_failure=True)
+
+  #######################################
+
+  def run_multiple_regulatory_domains(self, use_ib, init_domain, resp_domain):
+    """Verify that a data-path setup with two conflicting regulatory domains
+    works (the result should be run in Channel 6 - but that is not tested).
+
+    Args:
+      use_ib: True to use in-band discovery, False to use out-of-band discovery.
+      init_domain: The regulatory domain of the Initiator/Subscriber.
+      resp_domain: The regulator domain of the Responder/Publisher.
+    """
+    init_dut = self.android_devices[0]
+    resp_dut = self.android_devices[1]
+
+    init_dut.droid.wifiSetCountryCode(init_domain)
+    resp_dut.droid.wifiSetCountryCode(resp_domain)
+
+    if use_ib:
+      (resp_req_key, init_req_key, resp_aware_if, init_aware_if, resp_ipv6,
+       init_ipv6) = autils.create_ib_ndp(resp_dut, init_dut,
+                                         autils.create_discovery_config(
+                                           "GoogleTestXyz",
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED),
+                                         autils.create_discovery_config(
+                                           "GoogleTestXyz",
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE),
+                                         self.device_startup_offset)
+    else:
+      (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6,
+       resp_ipv6) = autils.create_oob_ndp(init_dut, resp_dut)
+
+    self.log.info("Interface names: I=%s, R=%s", init_aware_if, resp_aware_if)
+    self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+                  resp_ipv6)
+
+    # clean-up
+    resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+    init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+
+  @test_tracker_info(uuid="eff53739-35c5-47a6-81f0-d70b51d89c3b")
+  def test_multiple_regulator_domains_ib_us_jp(self):
+    """Verify data-path setup across multiple regulator domains.
+
+    - Uses in-band discovery
+    - Subscriber=US, Publisher=JP
+    """
+    self.run_multiple_regulatory_domains(
+        use_ib=True,
+        init_domain=wutils.WifiEnums.CountryCode.US,
+        resp_domain=wutils.WifiEnums.CountryCode.JAPAN)
+
+  @test_tracker_info(uuid="19af47cc-3204-40ef-b50f-14cf7b89cf4a")
+  def test_multiple_regulator_domains_ib_jp_us(self):
+    """Verify data-path setup across multiple regulator domains.
+
+    - Uses in-band discovery
+    - Subscriber=JP, Publisher=US
+    """
+    self.run_multiple_regulatory_domains(
+        use_ib=True,
+        init_domain=wutils.WifiEnums.CountryCode.JAPAN,
+        resp_domain=wutils.WifiEnums.CountryCode.US)
+
+  @test_tracker_info(uuid="65285ab3-977f-4dbd-b663-d5a02f4fc663")
+  def test_multiple_regulator_domains_oob_us_jp(self):
+    """Verify data-path setup across multiple regulator domains.
+
+    - Uses out-f-band discovery
+    - Initiator=US, Responder=JP
+    """
+    self.run_multiple_regulatory_domains(
+        use_ib=False,
+        init_domain=wutils.WifiEnums.CountryCode.US,
+        resp_domain=wutils.WifiEnums.CountryCode.JAPAN)
+
+  @test_tracker_info(uuid="8a417e24-aaf6-44b9-a089-a07c3ba8d954")
+  def test_multiple_regulator_domains_oob_jp_us(self):
+    """Verify data-path setup across multiple regulator domains.
+
+    - Uses out-of-band discovery
+    - Initiator=JP, Responder=US
+    """
+    self.run_multiple_regulatory_domains(
+        use_ib=False,
+        init_domain=wutils.WifiEnums.CountryCode.JAPAN,
+        resp_domain=wutils.WifiEnums.CountryCode.US)
+
+  ########################################################################
+
+  def run_mix_ib_oob(self, same_request, ib_first, inits_on_same_dut):
+    """Validate that multiple network requests issued using both in-band and
+    out-of-band discovery behave as expected.
+
+    The same_request parameter controls whether identical single NDP is
+    expected, if True, or whether multiple NDPs on different NDIs are expected,
+    if False.
+
+    Args:
+      same_request: Issue canonically identical requests (same NMI peer, same
+                    passphrase) if True, if False use different passphrases.
+      ib_first: If True then the in-band network is requested first, otherwise
+                (if False) then the out-of-band network is requested first.
+      inits_on_same_dut: If True then the Initiators are run on the same device,
+                         otherwise (if False) then the Initiators are run on
+                         different devices. Note that Subscribe == Initiator.
+    """
+    if not same_request:
+      asserts.skip_if(self.android_devices[0].aware_capabilities[
+                        aconsts.CAP_MAX_NDI_INTERFACES] < 2 or
+                      self.android_devices[1].aware_capabilities[
+                        aconsts.CAP_MAX_NDI_INTERFACES] < 2,
+                      "DUTs do not support enough NDIs")
+
+    (p_dut, s_dut, p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
+     peer_id_on_pub_null) = self.set_up_discovery(
+        aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE, False)
+
+    p_id2, p_mac = autils.attach_with_identity(p_dut)
+    s_id2, s_mac = autils.attach_with_identity(s_dut)
+
+    if inits_on_same_dut:
+      resp_dut = p_dut
+      resp_id = p_id2
+      resp_mac = p_mac
+
+      init_dut = s_dut
+      init_id = s_id2
+      init_mac = s_mac
+    else:
+      resp_dut = s_dut
+      resp_id = s_id2
+      resp_mac = s_mac
+
+      init_dut = p_dut
+      init_id = p_id2
+      init_mac = p_mac
+
+    passphrase = None if same_request else self.PASSPHRASE
+
+    if ib_first:
+      # request in-band network (to completion)
+      p_req_key = self.request_network(
+          p_dut,
+          p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, None))
+      s_req_key = self.request_network(
+          s_dut,
+          s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id,
+                                                      peer_id_on_sub))
+
+      # Publisher & Subscriber: wait for network formation
+      p_net_event = autils.wait_for_event_with_keys(
+          p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+          autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+          (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+      s_net_event = autils.wait_for_event_with_keys(
+          s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+          autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+          (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+    # request out-of-band network
+    resp_req_key = autils.request_network(resp_dut,
+          resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+              resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, passphrase))
+    init_req_key = autils.request_network(init_dut,
+          init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+              init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, passphrase))
+
+    resp_net_event = autils.wait_for_event_with_keys(
+        resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+        autils.EVENT_NDP_TIMEOUT,
+        (cconsts.NETWORK_CB_KEY_EVENT,
+         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+        (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+    init_net_event = autils.wait_for_event_with_keys(
+        init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+        autils.EVENT_NDP_TIMEOUT,
+        (cconsts.NETWORK_CB_KEY_EVENT,
+         cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+        (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+
+    if not ib_first:
+      # request in-band network (to completion)
+      p_req_key = self.request_network(
+          p_dut,
+          p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, None))
+      s_req_key = self.request_network(
+          s_dut,
+          s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id,
+                                                      peer_id_on_sub))
+
+      # Publisher & Subscriber: wait for network formation
+      p_net_event = autils.wait_for_event_with_keys(
+          p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+          autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+          (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+      s_net_event = autils.wait_for_event_with_keys(
+          s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+          autils.EVENT_NDP_TIMEOUT,
+          (cconsts.NETWORK_CB_KEY_EVENT,
+           cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+          (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+    # extract net info
+    pub_interface = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+    sub_interface = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+    resp_interface = resp_net_event["data"][
+      cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+    init_interface = init_net_event["data"][
+      cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+    self.log.info(
+        "Interface names: Pub=%s, Sub=%s, Resp=%s, Init=%s", pub_interface,
+        sub_interface, resp_interface, init_interface)
+
+    pub_ipv6 = \
+    p_dut.droid.connectivityGetLinkLocalIpv6Address(pub_interface).split("%")[0]
+    sub_ipv6 = \
+    s_dut.droid.connectivityGetLinkLocalIpv6Address(sub_interface).split("%")[0]
+    resp_ipv6 = \
+    resp_dut.droid.connectivityGetLinkLocalIpv6Address(resp_interface).split(
+      "%")[0]
+    init_ipv6 = \
+    init_dut.droid.connectivityGetLinkLocalIpv6Address(init_interface).split(
+      "%")[0]
+
+    self.log.info(
+      "Interface addresses (IPv6): Pub=%s, Sub=%s, Resp=%s, Init=%s", pub_ipv6,
+      sub_ipv6, resp_ipv6, init_ipv6)
+
+    # validate NDP/NDI conditions (using interface names & ipv6)
+    if same_request:
+      asserts.assert_equal(pub_interface,
+         resp_interface if inits_on_same_dut else init_interface,
+         "NDP interfaces don't match on Pub/other")
+      asserts.assert_equal(sub_interface,
+         init_interface if inits_on_same_dut else resp_interface,
+         "NDP interfaces don't match on Sub/other")
+
+      asserts.assert_equal(pub_ipv6,
+                           resp_ipv6 if inits_on_same_dut else init_ipv6,
+                           "NDP IPv6 don't match on Pub/other")
+      asserts.assert_equal(sub_ipv6,
+                           init_ipv6 if inits_on_same_dut else resp_ipv6,
+                           "NDP IPv6 don't match on Sub/other")
+    else:
+      asserts.assert_false(pub_interface == (
+        resp_interface if inits_on_same_dut else init_interface),
+                           "NDP interfaces match on Pub/other")
+      asserts.assert_false(sub_interface == (
+        init_interface if inits_on_same_dut else resp_interface),
+                           "NDP interfaces match on Sub/other")
+
+      asserts.assert_false(pub_ipv6 ==
+                           (resp_ipv6 if inits_on_same_dut else init_ipv6),
+                           "NDP IPv6 match on Pub/other")
+      asserts.assert_false(sub_ipv6 ==
+                           (init_ipv6 if inits_on_same_dut else resp_ipv6),
+                           "NDP IPv6 match on Sub/other")
+
+    # release requests
+    p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+    s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+    resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+    init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+
+  @test_tracker_info(uuid="d8a0839d-4ba0-43f2-af93-3cf1382f9f16")
+  def test_identical_ndps_mix_ib_oob_ib_first_same_polarity(self):
+    """Validate that a single NDP is created for multiple identical requests
+    which are issued through either in-band (ib) or out-of-band (oob) APIs.
+
+    The in-band request is issued first. Both Initiators (Sub == Initiator) are
+    run on the same device.
+    """
+    self.run_mix_ib_oob(same_request=True,
+                        ib_first=True,
+                        inits_on_same_dut=True)
+
+  @test_tracker_info(uuid="70bbb811-0bed-4a19-96b3-f2446e777c8a")
+  def test_identical_ndps_mix_ib_oob_oob_first_same_polarity(self):
+    """Validate that a single NDP is created for multiple identical requests
+    which are issued through either in-band (ib) or out-of-band (oob) APIs.
+
+    The out-of-band request is issued first. Both Initiators (Sub == Initiator)
+    are run on the same device.
+    """
+    self.run_mix_ib_oob(same_request=True,
+                        ib_first=False,
+                        inits_on_same_dut=True)
+
+  @test_tracker_info(uuid="d9796da5-f96a-4a51-be0f-89d6f5bfe3ad")
+  def test_identical_ndps_mix_ib_oob_ib_first_diff_polarity(self):
+    """Validate that a single NDP is created for multiple identical requests
+    which are issued through either in-band (ib) or out-of-band (oob) APIs.
+
+    The in-band request is issued first. Initiators (Sub == Initiator) are
+    run on different devices.
+    """
+    self.run_mix_ib_oob(same_request=True,
+                        ib_first=True,
+                        inits_on_same_dut=False)
+
+  @test_tracker_info(uuid="72b16cbf-53ad-4f98-8dcf-a8cc5fa812e3")
+  def test_identical_ndps_mix_ib_oob_oob_first_diff_polarity(self):
+    """Validate that a single NDP is created for multiple identical requests
+    which are issued through either in-band (ib) or out-of-band (oob) APIs.
+
+    The out-of-band request is issued first. Initiators (Sub == Initiator) are
+    run on different devices.
+    """
+    self.run_mix_ib_oob(same_request=True,
+                        ib_first=False,
+                        inits_on_same_dut=False)
+
+  @test_tracker_info(uuid="51f9581e-c5ee-48a7-84d2-adff4876c3d7")
+  def test_multiple_ndis_mix_ib_oob_ib_first_same_polarity(self):
+    """Validate that multiple NDIs are created for NDPs which are requested with
+    different security configurations. Use a mix of in-band and out-of-band APIs
+    to request the different NDPs.
+
+    The in-band request is issued first. Initiators (Sub == Initiator) are
+    run on the same device.
+    """
+    self.run_mix_ib_oob(same_request=False,
+                        ib_first=True,
+                        inits_on_same_dut=True)
+
+  @test_tracker_info(uuid="b1e3070e-4d38-4b31-862d-39b82e0f2853")
+  def test_multiple_ndis_mix_ib_oob_oob_first_same_polarity(self):
+    """Validate that multiple NDIs are created for NDPs which are requested with
+    different security configurations. Use a mix of in-band and out-of-band APIs
+    to request the different NDPs.
+
+    The out-of-band request is issued first. Initiators (Sub == Initiator) are
+    run on the same device.
+    """
+    self.run_mix_ib_oob(same_request=False,
+                        ib_first=False,
+                        inits_on_same_dut=True)
+
+  @test_tracker_info(uuid="b1e3070e-4d38-4b31-862d-39b82e0f2853")
+  def test_multiple_ndis_mix_ib_oob_ib_first_diff_polarity(self):
+    """Validate that multiple NDIs are created for NDPs which are requested with
+    different security configurations. Use a mix of in-band and out-of-band APIs
+    to request the different NDPs.
+
+    The in-band request is issued first. Initiators (Sub == Initiator) are
+    run on different devices.
+    """
+    self.run_mix_ib_oob(same_request=False,
+                        ib_first=True,
+                        inits_on_same_dut=False)
+
+  @test_tracker_info(uuid="596caadf-028e-494b-bbce-8304ccec2cbb")
+  def test_multiple_ndis_mix_ib_oob_oob_first_diff_polarity(self):
+    """Validate that multiple NDIs are created for NDPs which are requested with
+    different security configurations. Use a mix of in-band and out-of-band APIs
+    to request the different NDPs.
+
+    The out-of-band request is issued first. Initiators (Sub == Initiator) are
+    run on different devices.
+    """
+    self.run_mix_ib_oob(same_request=False,
+                        ib_first=False,
+                        inits_on_same_dut=False)
+
+  ########################################################################
+
+  def test_ndp_loop(self):
+    """Validate that can create a loop (chain) of N NDPs between N devices,
+    where N >= 3, e.g.
+
+    A - B
+    B - C
+    C - A
+
+    The NDPs are all OPEN (no encryption).
+    """
+    asserts.assert_true(len(self.android_devices) >= 3,
+                        'A minimum of 3 devices is needed to run the test, have %d' %
+                        len(self.android_devices))
+
+    duts = self.android_devices
+    loop_len = len(duts)
+    ids = []
+    macs = []
+    reqs = [[], [], []]
+    ifs = [[], [], []]
+    ipv6s = [[], [], []]
+
+    for i in range(loop_len):
+      duts[i].pretty_name = chr(ord("A") + i)
+
+    # start-up 3 devices (attach w/ identity)
+    for i in range(loop_len):
+      ids.append(duts[i].droid.wifiAwareAttach(True))
+      autils.wait_for_event(duts[i], aconsts.EVENT_CB_ON_ATTACHED)
+      ident_event = autils.wait_for_event(duts[i],
+                                          aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+      macs.append(ident_event['data']['mac'])
+
+    # wait for for devices to synchronize with each other - there are no other
+    # mechanisms to make sure this happens for OOB discovery (except retrying
+    # to execute the data-path request)
+    time.sleep(autils.WAIT_FOR_CLUSTER)
+
+    # create the N NDPs: i to (i+1) % N
+    for i in range(loop_len):
+      peer_device = (i + 1) % loop_len
+
+      (init_req_key, resp_req_key, init_aware_if,
+       resp_aware_if, init_ipv6, resp_ipv6) = autils.create_oob_ndp_on_sessions(
+          duts[i], duts[peer_device],
+          ids[i], macs[i], ids[peer_device], macs[peer_device])
+
+      reqs[i].append(init_req_key)
+      reqs[peer_device].append(resp_req_key)
+      ifs[i].append(init_aware_if)
+      ifs[peer_device].append(resp_aware_if)
+      ipv6s[i].append(init_ipv6)
+      ipv6s[peer_device].append(resp_ipv6)
+
+    # clean-up
+    for i in range(loop_len):
+      for req in reqs[i]:
+        duts[i].droid.connectivityUnregisterNetworkCallback(req)
+
+    # info
+    self.log.info("MACs: %s", macs)
+    self.log.info("Interface names: %s", ifs)
+    self.log.info("IPv6 addresses: %s", ipv6s)
+    asserts.explicit_pass("NDP loop test",
+                          extras={"macs": macs, "ifs": ifs, "ipv6s": ipv6s})
diff --git a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py b/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
index 1784d12..c6f75b0 100644
--- a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
+++ b/acts/tests/google/wifi/aware/functional/DiscoveryTest.py
@@ -831,7 +831,7 @@
         s_mf_1="goodbye there string")
 
   #######################################
-  # Multiple concurrent services key
+  # Multiple concurrent services
   #######################################
 
   def run_multiple_concurrent_services(self, type_x, type_y):
@@ -957,6 +957,7 @@
         event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], y_msg,
         "Message on service Y from DUT2 to DUT1 not received correctly")
 
+  @test_tracker_info(uuid="eef80cf3-1fd2-4526-969b-6af2dce785d7")
   def test_multiple_concurrent_services_both_unsolicited_passive(self):
     """Validate multiple concurrent discovery sessions running on both devices.
     - DUT1 & DUT2 running Publish for X
@@ -974,6 +975,7 @@
       type_x=[aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE],
       type_y=[aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE])
 
+  @test_tracker_info(uuid="46739f04-ab2b-4556-b1a4-9aa2774869b5")
   def test_multiple_concurrent_services_both_solicited_active(self):
     """Validate multiple concurrent discovery sessions running on both devices.
     - DUT1 & DUT2 running Publish for X
@@ -991,6 +993,7 @@
       type_x=[aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE],
       type_y=[aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE])
 
+  @test_tracker_info(uuid="5f8f7fd2-4a0e-4cca-8cbb-6d54353f2baa")
   def test_multiple_concurrent_services_mix_unsolicited_solicited(self):
     """Validate multiple concurrent discovery sessions running on both devices.
     - DUT1 & DUT2 running Publish for X
@@ -1008,3 +1011,25 @@
     self.run_multiple_concurrent_services(
       type_x=[aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE],
       type_y=[aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE])
+
+  #########################################################
+
+  @test_tracker_info(uuid="908ec896-fc7a-4ee4-b633-a2f042b74448")
+  def test_upper_lower_service_name_equivalence(self):
+    """Validate that Service Name is case-insensitive. Publish a service name
+    with mixed case, subscribe to the same service name with alternative case
+    and verify that discovery happens."""
+    p_dut = self.android_devices[0]
+    s_dut = self.android_devices[1]
+
+    pub_service_name = "GoogleAbCdEf"
+    sub_service_name = "GoogleaBcDeF"
+
+    autils.create_discovery_pair(p_dut, s_dut,
+                               p_config=autils.create_discovery_config(
+                                 pub_service_name,
+                                 aconsts.PUBLISH_TYPE_UNSOLICITED),
+                               s_config=autils.create_discovery_config(
+                                 sub_service_name,
+                                 aconsts.SUBSCRIBE_TYPE_PASSIVE),
+                               device_startup_offset=self.device_startup_offset)
diff --git a/acts/tests/google/wifi/aware/functional/MacRandomTest.py b/acts/tests/google/wifi/aware/functional/MacRandomTest.py
index 329ead4..af1503b 100644
--- a/acts/tests/google/wifi/aware/functional/MacRandomTest.py
+++ b/acts/tests/google/wifi/aware/functional/MacRandomTest.py
@@ -57,6 +57,10 @@
     (NAN data-interface) on each enable/disable cycle"""
     dut = self.android_devices[0]
 
+    # re-enable randomization interval (since if disabled it may also disable
+    # the 'randomize on enable' feature).
+    autils.configure_mac_random_interval(dut, 1800)
+
     # DUT: attach and wait for confirmation & identity 10 times
     mac_addresses = {}
     for i in range(self.NUM_ITERATIONS):
@@ -108,9 +112,8 @@
 
     dut = self.android_devices[0]
 
-    # set randomization interval to 5 seconds
-    dut.adb.shell("cmd wifiaware native_api set mac_random_interval_sec %d" %
-                  RANDOM_INTERVAL)
+    # set randomization interval to 120 seconds
+    autils.configure_mac_random_interval(dut, RANDOM_INTERVAL)
 
     # attach and wait for first identity
     id = dut.droid.wifiAwareAttach(True)
diff --git a/acts/tests/google/wifi/aware/performance/LatencyTest.py b/acts/tests/google/wifi/aware/performance/LatencyTest.py
index bde9ff4..bfadebc 100644
--- a/acts/tests/google/wifi/aware/performance/LatencyTest.py
+++ b/acts/tests/google/wifi/aware/performance/LatencyTest.py
@@ -92,8 +92,8 @@
     s_dut.pretty_name = "Subscriber"
 
     # override the default DW configuration
-    autils.config_dw_all_modes(p_dut, dw_24ghz, dw_5ghz)
-    autils.config_dw_all_modes(s_dut, dw_24ghz, dw_5ghz)
+    autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+    autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
 
     latencies = []
     failed_discoveries = 0
@@ -174,8 +174,8 @@
     s_dut.pretty_name = "Subscriber"
 
     # override the default DW configuration
-    autils.config_dw_all_modes(p_dut, dw_24ghz, dw_5ghz)
-    autils.config_dw_all_modes(s_dut, dw_24ghz, dw_5ghz)
+    autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+    autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
 
     # Publisher+Subscriber: attach and wait for confirmation
     p_id = p_dut.droid.wifiAwareAttach(False)
@@ -253,8 +253,8 @@
     s_dut = self.android_devices[1]
 
     # override the default DW configuration
-    autils.config_dw_all_modes(p_dut, dw_24ghz, dw_5ghz)
-    autils.config_dw_all_modes(s_dut, dw_24ghz, dw_5ghz)
+    autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+    autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
 
     # Start up a discovery session
     (p_id, s_id, p_disc_id, s_disc_id,
@@ -341,8 +341,8 @@
     resp_dut.pretty_name = 'Responder'
 
     # override the default DW configuration
-    autils.config_dw_all_modes(init_dut, dw_24ghz, dw_5ghz)
-    autils.config_dw_all_modes(resp_dut, dw_24ghz, dw_5ghz)
+    autils.config_power_settings(init_dut, dw_24ghz, dw_5ghz)
+    autils.config_power_settings(resp_dut, dw_24ghz, dw_5ghz)
 
     # Initiator+Responder: attach and wait for confirmation & identity
     init_id = init_dut.droid.wifiAwareAttach(True)
@@ -427,6 +427,209 @@
                                                                      dw_5ghz))
     results[key_avail]["ndp_setup_failures"] = ndp_setup_failures
 
+  def run_end_to_end_latency(self, results, dw_24ghz, dw_5ghz, num_iterations,
+      startup_offset, include_setup):
+    """Measure the latency for end-to-end communication link setup:
+    - Start Aware
+    - Discovery
+    - Message from Sub -> Pub
+    - Message from Pub -> Sub
+    - NDP setup
+
+    Args:
+      results: Result array to be populated - will add results (not erase it)
+      dw_24ghz: DW interval in the 2.4GHz band.
+      dw_5ghz: DW interval in the 5GHz band.
+      startup_offset: The start-up gap (in seconds) between the two devices
+      include_setup: True to include the cluster setup in the latency
+                    measurements.
+    """
+    key = "dw24_%d_dw5_%d" % (dw_24ghz, dw_5ghz)
+    results[key] = {}
+    results[key]["num_iterations"] = num_iterations
+
+    p_dut = self.android_devices[0]
+    p_dut.pretty_name = "Publisher"
+    s_dut = self.android_devices[1]
+    s_dut.pretty_name = "Subscriber"
+
+    # override the default DW configuration
+    autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+    autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
+
+    latencies = []
+
+    # allow for failures here since running lots of samples and would like to
+    # get the partial data even in the presence of errors
+    failures = 0
+
+    if not include_setup:
+      # Publisher+Subscriber: attach and wait for confirmation
+      p_id = p_dut.droid.wifiAwareAttach(False)
+      autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+      time.sleep(startup_offset)
+      s_id = s_dut.droid.wifiAwareAttach(False)
+      autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+    for i in range(num_iterations):
+      while (True): # for pseudo-goto/finalize
+        timestamp_start = time.perf_counter()
+
+        if include_setup:
+          # Publisher+Subscriber: attach and wait for confirmation
+          p_id = p_dut.droid.wifiAwareAttach(False)
+          autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+          time.sleep(startup_offset)
+          s_id = s_dut.droid.wifiAwareAttach(False)
+          autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+        # start publish
+        p_disc_id, p_disc_event = self.start_discovery_session(
+            p_dut, p_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED)
+
+        # start subscribe
+        s_disc_id, s_session_event = self.start_discovery_session(
+            s_dut, s_id, False, aconsts.SUBSCRIBE_TYPE_PASSIVE)
+
+        # wait for discovery (allow for failures here since running lots of
+        # samples and would like to get the partial data even in the presence of
+        # errors)
+        try:
+          event = s_dut.ed.pop_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+                                     autils.EVENT_TIMEOUT)
+          s_dut.log.info("[Subscriber] SESSION_CB_ON_SERVICE_DISCOVERED: %s",
+                         event["data"])
+          peer_id_on_sub = event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+        except queue.Empty:
+          s_dut.log.info("[Subscriber] Timed out while waiting for "
+                         "SESSION_CB_ON_SERVICE_DISCOVERED")
+          failures = failures + 1
+          break
+
+        # message from Sub -> Pub
+        msg_s2p = "Message Subscriber -> Publisher #%d" % i
+        next_msg_id = self.get_next_msg_id()
+        s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, next_msg_id,
+                                         msg_s2p, 0)
+
+        # wait for Tx confirmation
+        try:
+          s_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_SENT,
+                             autils.EVENT_TIMEOUT)
+        except queue.Empty:
+          s_dut.log.info("[Subscriber] Timed out while waiting for "
+                         "SESSION_CB_ON_MESSAGE_SENT")
+          failures = failures + 1
+          break
+
+        # wait for Rx confirmation (and validate contents)
+        try:
+          event = p_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+                                     autils.EVENT_TIMEOUT)
+          peer_id_on_pub = event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+          if (event["data"][
+            aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING] != msg_s2p):
+            p_dut.log.info("[Publisher] Corrupted input message - %s", event)
+            failures = failures + 1
+            break
+        except queue.Empty:
+          p_dut.log.info("[Publisher] Timed out while waiting for "
+                         "SESSION_CB_ON_MESSAGE_RECEIVED")
+          failures = failures + 1
+          break
+
+        # message from Pub -> Sub
+        msg_p2s = "Message Publisher -> Subscriber #%d" % i
+        next_msg_id = self.get_next_msg_id()
+        p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub, next_msg_id,
+                                         msg_p2s, 0)
+
+        # wait for Tx confirmation
+        try:
+          p_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_SENT,
+                             autils.EVENT_TIMEOUT)
+        except queue.Empty:
+          p_dut.log.info("[Publisher] Timed out while waiting for "
+                         "SESSION_CB_ON_MESSAGE_SENT")
+          failures = failures + 1
+          break
+
+        # wait for Rx confirmation (and validate contents)
+        try:
+          event = s_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+                                     autils.EVENT_TIMEOUT)
+          if (event["data"][
+            aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING] != msg_p2s):
+            s_dut.log.info("[Subscriber] Corrupted input message - %s", event)
+            failures = failures + 1
+            break
+        except queue.Empty:
+          s_dut.log.info("[Subscriber] Timed out while waiting for "
+                         "SESSION_CB_ON_MESSAGE_RECEIVED")
+          failures = failures + 1
+          break
+
+        # create NDP
+
+        # Publisher: request network
+        p_req_key = autils.request_network(
+            p_dut,
+            p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id,
+                                                        peer_id_on_pub, None))
+
+        # Subscriber: request network
+        s_req_key = autils.request_network(
+            s_dut,
+            s_dut.droid.wifiAwareCreateNetworkSpecifier(s_disc_id,
+                                                        peer_id_on_sub, None))
+
+        # Publisher & Subscriber: wait for network formation
+        try:
+          p_net_event = autils.wait_for_event_with_keys(
+              p_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT, (
+              cconsts.NETWORK_CB_KEY_EVENT,
+              cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+              (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+          s_net_event = autils.wait_for_event_with_keys(
+              s_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT, (
+              cconsts.NETWORK_CB_KEY_EVENT,
+              cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+              (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+        except:
+          failures = failures + 1
+          break
+
+        p_aware_if = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+        s_aware_if = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+        p_ipv6 = \
+        p_dut.droid.connectivityGetLinkLocalIpv6Address(p_aware_if).split("%")[
+          0]
+        s_ipv6 = \
+        s_dut.droid.connectivityGetLinkLocalIpv6Address(s_aware_if).split("%")[
+          0]
+
+        p_dut.log.info("[Publisher] IF=%s, IPv6=%s", p_aware_if, p_ipv6)
+        s_dut.log.info("[Subscriber] IF=%s, IPv6=%s", s_aware_if, s_ipv6)
+
+        latencies.append(time.perf_counter() - timestamp_start)
+        break
+
+      # destroy sessions
+      p_dut.droid.wifiAwareDestroyDiscoverySession(p_disc_id)
+      s_dut.droid.wifiAwareDestroyDiscoverySession(s_disc_id)
+      if include_setup:
+        p_dut.droid.wifiAwareDestroy(p_id)
+        s_dut.droid.wifiAwareDestroy(s_id)
+
+    autils.extract_stats(
+        p_dut,
+        data=latencies,
+        results=results[key],
+        key_prefix="",
+        log_prefix="End-to-End(dw24=%d, dw5=%d)" % (dw_24ghz, dw_5ghz))
+    results[key]["failures"] = failures
+
 
   ########################################################################
 
@@ -438,8 +641,8 @@
       self.run_synchronization_latency(
           results=results,
           do_unsolicited_passive=True,
-          dw_24ghz=aconsts.DW_24_INTERACTIVE,
-          dw_5ghz=aconsts.DW_5_INTERACTIVE,
+          dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+          dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
           num_iterations=10,
           startup_offset=startup_offset,
           timeout_period=20)
@@ -454,8 +657,8 @@
       self.run_synchronization_latency(
           results=results,
           do_unsolicited_passive=True,
-          dw_24ghz=aconsts.DW_24_NON_INTERACTIVE,
-          dw_5ghz=aconsts.DW_5_NON_INTERACTIVE,
+          dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+          dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
           num_iterations=10,
           startup_offset=startup_offset,
           timeout_period=20)
@@ -469,8 +672,8 @@
     self.run_discovery_latency(
         results=results,
         do_unsolicited_passive=True,
-        dw_24ghz=aconsts.DW_24_INTERACTIVE,
-        dw_5ghz=aconsts.DW_5_INTERACTIVE,
+        dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+        dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
         num_iterations=100)
     asserts.explicit_pass(
         "test_discovery_latency_default_parameters finished", extras=results)
@@ -482,8 +685,8 @@
     self.run_discovery_latency(
         results=results,
         do_unsolicited_passive=True,
-        dw_24ghz=aconsts.DW_24_NON_INTERACTIVE,
-        dw_5ghz=aconsts.DW_5_NON_INTERACTIVE,
+        dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+        dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
         num_iterations=100)
     asserts.explicit_pass(
         "test_discovery_latency_non_interactive_dws finished", extras=results)
@@ -510,8 +713,8 @@
     results = {}
     self.run_message_latency(
         results=results,
-        dw_24ghz=aconsts.DW_24_INTERACTIVE,
-        dw_5ghz=aconsts.DW_5_INTERACTIVE,
+        dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+        dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
         num_iterations=100)
     asserts.explicit_pass(
         "test_message_latency_default_dws finished", extras=results)
@@ -524,8 +727,8 @@
     results = {}
     self.run_message_latency(
         results=results,
-        dw_24ghz=aconsts.DW_24_NON_INTERACTIVE,
-        dw_5ghz=aconsts.DW_5_NON_INTERACTIVE,
+        dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+        dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
         num_iterations=100)
     asserts.explicit_pass(
         "test_message_latency_non_interactive_dws finished", extras=results)
@@ -536,8 +739,8 @@
     results = {}
     self.run_ndp_oob_latency(
         results=results,
-        dw_24ghz=aconsts.DW_24_INTERACTIVE,
-        dw_5ghz=aconsts.DW_5_INTERACTIVE,
+        dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+        dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
         num_iterations=100)
     asserts.explicit_pass(
         "test_ndp_setup_latency_default_dws finished", extras=results)
@@ -549,8 +752,49 @@
     results = {}
     self.run_ndp_oob_latency(
         results=results,
-        dw_24ghz=aconsts.DW_24_NON_INTERACTIVE,
-        dw_5ghz=aconsts.DW_5_NON_INTERACTIVE,
+        dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+        dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
         num_iterations=100)
     asserts.explicit_pass(
         "test_ndp_setup_latency_non_interactive_dws finished", extras=results)
+
+  def test_end_to_end_latency_default_dws(self):
+    """Measure the latency for end-to-end communication link setup:
+      - Start Aware
+      - Discovery
+      - Message from Sub -> Pub
+      - Message from Pub -> Sub
+      - NDP setup
+    """
+    results = {}
+    self.run_end_to_end_latency(
+        results,
+        dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+        dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+        num_iterations=10,
+        startup_offset=0,
+        include_setup=True)
+    asserts.explicit_pass(
+        "test_end_to_end_latency_default_dws finished", extras=results)
+
+  def test_end_to_end_latency_post_attach_default_dws(self):
+    """Measure the latency for end-to-end communication link setup without
+    the initial synchronization:
+      - Start Aware & synchronize initially
+      - Loop:
+        - Discovery
+        - Message from Sub -> Pub
+        - Message from Pub -> Sub
+        - NDP setup
+    """
+    results = {}
+    self.run_end_to_end_latency(
+        results,
+        dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+        dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+        num_iterations=10,
+        startup_offset=0,
+        include_setup=False)
+    asserts.explicit_pass(
+      "test_end_to_end_latency_post_attach_default_dws finished",
+      extras=results)
diff --git a/acts/tests/google/wifi/aware/performance/ThroughputTest.py b/acts/tests/google/wifi/aware/performance/ThroughputTest.py
index 6cf1046..ddb6d15 100644
--- a/acts/tests/google/wifi/aware/performance/ThroughputTest.py
+++ b/acts/tests/google/wifi/aware/performance/ThroughputTest.py
@@ -36,7 +36,7 @@
   PASSPHRASE2 = "This is some random passphrase - very very secure - but diff!!"
 
   def __init__(self, controllers):
-    AwareBaseTest.__init__(self, controllers)
+    super(ThroughputTest, self).__init__(controllers)
 
   def request_network(self, dut, ns):
     """Request a Wi-Fi Aware network.
@@ -302,12 +302,12 @@
 
       # Wait for network
       init_net_event = autils.wait_for_event_with_keys(
-          init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
+          init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
           (cconsts.NETWORK_CB_KEY_EVENT,
            cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
           (cconsts.NETWORK_CB_KEY_ID, init_req_key))
       resp_net_event = autils.wait_for_event_with_keys(
-          resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
+          resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
           (cconsts.NETWORK_CB_KEY_EVENT,
            cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
           (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
diff --git a/acts/tests/google/wifi/aware/stress/DataPathStressTest.py b/acts/tests/google/wifi/aware/stress/DataPathStressTest.py
index 9a862cb..f718007 100644
--- a/acts/tests/google/wifi/aware/stress/DataPathStressTest.py
+++ b/acts/tests/google/wifi/aware/stress/DataPathStressTest.py
@@ -18,6 +18,7 @@
 import time
 
 from acts import asserts
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.net import connectivity_const as cconsts
 from acts.test_utils.wifi.aware import aware_const as aconsts
 from acts.test_utils.wifi.aware import aware_test_utils as autils
@@ -30,16 +31,29 @@
   ATTACH_ITERATIONS = 2
 
   # Number of iterations on create/destroy NDP in each discovery session.
-  NDP_ITERATIONS = 20
+  NDP_ITERATIONS = 50
+
+  # Maximum percentage of NDP setup failures over all iterations
+  MAX_FAILURE_PERCENTAGE = 1
 
   def __init__(self, controllers):
     AwareBaseTest.__init__(self, controllers)
 
   ################################################################
 
-  def test_oob_ndp_stress(self):
+  def run_oob_ndp_stress(self, attach_iterations, ndp_iterations,
+      trigger_failure_on_index=None):
     """Run NDP (NAN data-path) stress test creating and destroying Aware
-    attach sessions, discovery sessions, and NDPs."""
+    attach sessions, discovery sessions, and NDPs.
+
+    Args:
+      attach_iterations: Number of attach sessions.
+      ndp_iterations: Number of NDP to be attempted per attach session.
+      trigger_failure_on_index: Trigger a failure on this NDP iteration (the
+                                mechanism is to request NDP on Initiator
+                                before issuing the requeest on the Responder).
+                                If None then no artificial failure triggered.
+    """
     init_dut = self.android_devices[0]
     init_dut.pretty_name = 'Initiator'
     resp_dut = self.android_devices[1]
@@ -50,7 +64,7 @@
     ndp_resp_setup_success = 0
     ndp_resp_setup_failures = 0
 
-    for attach_iter in range(self.ATTACH_ITERATIONS):
+    for attach_iter in range(attach_iterations):
       init_id = init_dut.droid.wifiAwareAttach(True)
       autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
       init_ident_event = autils.wait_for_event(
@@ -68,18 +82,41 @@
       # to execute the data-path request)
       time.sleep(autils.WAIT_FOR_CLUSTER)
 
-      for ndp_iteration in range(self.NDP_ITERATIONS):
-        # Responder: request network
-        resp_req_key = autils.request_network(
-            resp_dut,
-            resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
-                resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
+      for ndp_iteration in range(ndp_iterations):
+        if trigger_failure_on_index != ndp_iteration:
+          # Responder: request network
+          resp_req_key = autils.request_network(
+              resp_dut,
+              resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+                  resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
 
-        # Initiator: request network
-        init_req_key = autils.request_network(
-            init_dut,
-            init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
-                init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
+          # Wait a minimal amount of time to let the Responder configure itself
+          # and be ready for the request. While calling it first may be
+          # sufficient there are no guarantees that a glitch may slow the
+          # Responder slightly enough to invert the setup order.
+          time.sleep(1)
+
+          # Initiator: request network
+          init_req_key = autils.request_network(
+              init_dut,
+              init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+                  init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
+        else:
+          # Initiator: request network
+          init_req_key = autils.request_network(
+              init_dut,
+              init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+                  init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
+
+          # Wait a minimal amount of time to let the Initiator configure itself
+          # to guarantee failure!
+          time.sleep(2)
+
+          # Responder: request network
+          resp_req_key = autils.request_network(
+              resp_dut,
+              resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+                  resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
 
         # Initiator: wait for network formation
         got_on_available = False
@@ -138,9 +175,31 @@
     results['ndp_init_setup_failures'] = ndp_init_setup_failures
     results['ndp_resp_setup_success'] = ndp_resp_setup_success
     results['ndp_resp_setup_failures'] = ndp_resp_setup_failures
-    asserts.assert_equal(
-        ndp_init_setup_failures + ndp_resp_setup_failures,
-        0,
-        'test_oob_ndp_stress finished',
-        extras=results)
-    asserts.explicit_pass("test_oob_ndp_stress done", extras=results)
+    max_failures = (
+        self.MAX_FAILURE_PERCENTAGE * attach_iterations * ndp_iterations / 100)
+    if max_failures == 0:
+      max_failures = 1
+    if trigger_failure_on_index is not None:
+      max_failures = max_failures + 1 # for the triggered failure
+    asserts.assert_true(
+      (ndp_init_setup_failures + ndp_resp_setup_failures) < (2 * max_failures),
+      'NDP setup failure rate exceeds threshold', extras=results)
+    asserts.explicit_pass("test_oob_ndp_stress* done", extras=results)
+
+  @test_tracker_info(uuid="a20a96ba-e71f-4d31-b850-b88a75381981")
+  def test_oob_ndp_stress(self):
+    """Run NDP (NAN data-path) stress test creating and destroying Aware
+    attach sessions, discovery sessions, and NDPs."""
+    self.run_oob_ndp_stress(self.ATTACH_ITERATIONS, self.NDP_ITERATIONS)
+
+  @test_tracker_info(uuid="1fb4a383-bf1a-411a-a904-489dd9e29c6a")
+  def test_oob_ndp_stress_failure_case(self):
+    """Run NDP (NAN data-path) stress test creating and destroying Aware
+    attach sessions, discovery sessions, and NDPs.
+
+    Verify recovery from failure by triggering an artifical failure and
+    verifying that all subsequent iterations succeed.
+    """
+    self.run_oob_ndp_stress(attach_iterations=1,
+                            ndp_iterations=10,
+                            trigger_failure_on_index=3)
diff --git a/acts/tests/google/wifi/aware/stress/DiscoveryStressTest.py b/acts/tests/google/wifi/aware/stress/DiscoveryStressTest.py
index 8b3d925..eaa5d19 100644
--- a/acts/tests/google/wifi/aware/stress/DiscoveryStressTest.py
+++ b/acts/tests/google/wifi/aware/stress/DiscoveryStressTest.py
@@ -15,9 +15,9 @@
 #   limitations under the License.
 
 import queue
-import time
 
 from acts import asserts
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.wifi.aware import aware_const as aconsts
 from acts.test_utils.wifi.aware import aware_test_utils as autils
 from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
@@ -37,6 +37,7 @@
 
   ####################################################################
 
+  @test_tracker_info(uuid="783791e5-7726-44e0-ac5b-98c1dbf493cb")
   def test_discovery_stress(self):
     """Create and destroy a random array of discovery sessions, up to the
     limit of capabilities."""
@@ -101,9 +102,6 @@
     results = {}
     results['discovery_setup_success'] = discovery_setup_success
     results['discovery_setup_fail'] = discovery_setup_fail
-    asserts.assert_equal(
-        discovery_setup_fail,
-        0,
-        'test_discovery_stress finished',
-        extras=results)
+    asserts.assert_equal(discovery_setup_fail, 0,
+                         'Discovery setup failures', extras=results)
     asserts.explicit_pass('test_discovery_stress done', extras=results)
diff --git a/acts/tests/google/wifi/aware/stress/MessagesStressTest.py b/acts/tests/google/wifi/aware/stress/MessagesStressTest.py
index 5871d61..34827f1 100644
--- a/acts/tests/google/wifi/aware/stress/MessagesStressTest.py
+++ b/acts/tests/google/wifi/aware/stress/MessagesStressTest.py
@@ -17,6 +17,7 @@
 import queue
 
 from acts import asserts
+from acts.test_decorators import test_tracker_info
 from acts.test_utils.wifi.aware import aware_const as aconsts
 from acts.test_utils.wifi.aware import aware_test_utils as autils
 from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
@@ -29,7 +30,19 @@
 
 class MessagesStressTest(AwareBaseTest):
   """Set of stress tests for Wi-Fi Aware L2 (layer 2) message exchanges."""
+
+  # Number of iterations in the stress test (number of messages)
   NUM_ITERATIONS = 100
+
+  # Maximum permitted percentage of messages which fail to be transmitted
+  # correctly
+  MAX_TX_FAILURE_PERCENTAGE = 2
+
+  # Maximum permitted percentage of messages which are received more than once
+  # (indicating, most likely, that the ACK wasn't received and the message was
+  # retransmitted)
+  MAX_DUPLICATE_RX_PERCENTAGE = 2
+
   SERVICE_NAME = "GoogleTestServiceXY"
 
   def __init__(self, controllers):
@@ -184,6 +197,7 @@
 
   #######################################################################
 
+  @test_tracker_info(uuid="e88c060f-4ca7-41c1-935a-d3d62878ec0b")
   def test_stress_message(self):
     """Stress test for bi-directional message transmission and reception."""
     p_dut = self.android_devices[0]
@@ -250,6 +264,9 @@
     # clear errors
     asserts.assert_equal(results["tx_unknown_ids"], 0, "Message ID corruption",
                          results)
+    asserts.assert_equal(results["tx_count_neither"], 0,
+                         "Tx message with no success or fail indication",
+                         results)
     asserts.assert_equal(results["tx_count_duplicate_fail"], 0,
                          "Duplicate Tx fail messages", results)
     asserts.assert_equal(results["tx_count_duplicate_success"], 0,
@@ -266,4 +283,14 @@
     asserts.assert_equal(results["rx_count_fail_tx_indication"], 0,
                          "Message received but Tx didn't get ACK", results)
 
-    asserts.explicit_pass("test_stress_message done", extras=results)
+    # permissible failures based on thresholds
+    asserts.assert_true(results["tx_count_fail"] <= (
+          self.MAX_TX_FAILURE_PERCENTAGE * self.NUM_ITERATIONS / 100),
+                        "Number of Tx failures exceeds threshold",
+                        extras=results)
+    asserts.assert_true(results["rx_count_duplicate"] <= (
+        self.MAX_DUPLICATE_RX_PERCENTAGE * self.NUM_ITERATIONS / 100),
+                        "Number of duplicate Rx exceeds threshold",
+                        extras=results)
+
+    asserts.explicit_pass("test_stress_message done", extras=results)
\ No newline at end of file
diff --git a/acts/tests/google/wifi/example_config.json b/acts/tests/google/wifi/example_config.json
new file mode 100644
index 0000000..42b0be7
--- /dev/null
+++ b/acts/tests/google/wifi/example_config.json
@@ -0,0 +1,38 @@
+{
+    "_description": "This and example IOT WiFi testbed.",
+    "testbed": [
+        {
+            "_description": "WiFi testbed with 1 devices",
+            "name": "<test station name>",
+            "AndroidDevice": [
+                "<device serial>"
+            ],
+            "IPerfServer": [
+                5005
+            ]
+        }
+    ],
+    "logpath": "/tmp/ACTS_logs",
+    "testpaths": [
+        "<path to acts root>/tools/test/connectivity/acts/tests/google/wifi"
+    ],
+    "iot_networks": [
+        {
+            "SSID": "<your SSID 2G>",
+            "password": "<your password>"
+        },
+        {
+            "SSID": "<your SSID 5G>",
+            "password": "<your password>"
+        },
+        {
+            "SSID": "<your SSID 2G 2>",
+            "password": "<your password>"
+        },
+        {
+            "SSID": "<your SSID 5G 2>",
+            "password": "<your password>"
+        }
+    ],
+    "iperf_server_address": "<your IP address>"
+}
\ No newline at end of file
diff --git a/acts/tests/google/wifi/rtt/README.md b/acts/tests/google/wifi/rtt/README.md
new file mode 100644
index 0000000..639c3d8
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/README.md
@@ -0,0 +1,56 @@
+# Wi-Fi RTT (IEEE 802.11mc) Integrated (ACTS/sl4a) Test Suite
+
+This directory contains ACTS/sl4a test scripts to verify and characterize
+the Wi-Fi RTT (IEEE 802.11mc) implementation in Android.
+
+There are 2 groups of tests (in 2 sub-directories):
+
+* functional: Functional tests that each implementation must pass. These
+are pass/fail tests.
+* stress: Tests which run through a large number of iterations to stress
+test the implementation. Considering that some failures are expected,
+especially in an over-the-air situation, pass/fail criteria are either
+not provided or may not apply to all implementations or test environments.
+
+The tests can be executed using:
+
+`act.py -c <config> -tc {<test_class>|<test_class>:<test_name>}`
+
+Where a test file is any of the `.py` files in any of the test sub-directories.
+If a test class is specified, then all tests within that test class are executed.
+
+## Test Beds
+The Wi-Fi RTT tests support several different test scenarios which require different test bed
+configuration. The test beds and their corresponding test files are:
+
+* Device Under Test + AP which supports IEEE 802.11mc
+  * functional/RangeApSupporting11McTest.py
+  * functional/RttRequestManagementTest.py
+  * functional/RttDisableTest.py
+  * stress/StressRangeApTest.py
+* Device Under Test + AP which does **not** support IEEE 802.11mc
+  * functional/RangeApNonSupporting11McTest.py
+* 2 Devices Under Test
+  * functional/RangeAwareTest.py
+  * functional/AwareDiscoveryWithRangingTest.py
+  * functional/RangeSoftApTest.py
+  * stress/StressRangeAwareTest.py
+
+## Test Configurations
+The test configuration, the `<config>` in the commands above, is stored in
+the *config* sub-directory. The configuration simply uses all connected
+devices without listing specific serial numbers. Note that some tests use a
+single device while others use 2 devices.
+
+The only provided configuration is *wifi_rtt.json*.
+
+The configuration defines the following keys to configure the test:
+
+* **lci_reference**, **lcr_reference**: Arrays of bytes used to validate that the *correct* LCI and
+LCR were received from the AP. These are empty by default and should be configured to match the
+configuration of the AP used in the test.
+* **rtt_reference_distance_mm**: The reference distance, in mm, between the test device and the test
+AP or between the two test devices (for Aware ranging tests).
+* **stress_test_min_iteration_count**, **stress_test_target_run_time_sec**: Parameters used to
+control the length and duration of the stress tests. The stress test runs for the specified number
+of iterations or for the specified duration - whichever is longer.
diff --git a/acts/tests/google/wifi/rtt/config/wifi_rtt.json b/acts/tests/google/wifi/rtt/config/wifi_rtt.json
new file mode 100644
index 0000000..41f77dc
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/config/wifi_rtt.json
@@ -0,0 +1,20 @@
+{
+    "_description": "This is a test configuration file for Wi-Fi RTT tests.",
+    "testbed":
+    [
+        {
+            "_description": "Wi-Fi RTT testbed: auto-detect all attached devices",
+            "name": "WifiRttAllAttached",
+            "AndroidDevice": "*"
+        }
+    ],
+    "logpath": "~/logs",
+    "testpaths": ["./tools/test/connectivity/acts/tests/google/wifi"],
+    "adb_logcat_param": "-b all",
+    "aware_default_power_mode": "INTERACTIVE",
+    "lci_reference": [],
+    "lcr_reference": [],
+    "rtt_reference_distance_mm": 100,
+    "stress_test_min_iteration_count": 100,
+    "stress_test_target_run_time_sec" : 30
+}
diff --git a/acts/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py b/acts/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py
new file mode 100644
index 0000000..f6d7c8d
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py
@@ -0,0 +1,1567 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import sys
+import time
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.net import connectivity_const as cconsts
+from acts.test_utils.wifi.aware import aware_const as aconsts
+from acts.test_utils.wifi.aware import aware_test_utils as autils
+from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class AwareDiscoveryWithRangingTest(AwareBaseTest, RttBaseTest):
+  """Set of tests for Wi-Fi Aware discovery configured with ranging (RTT)."""
+
+  SERVICE_NAME = "GoogleTestServiceRRRRR"
+
+  # Flag indicating whether the device has a limitation that does not allow it
+  # to execute Aware-based Ranging (whether direct or as part of discovery)
+  # whenever NDP is enabled.
+  RANGING_NDP_CONCURRENCY_LIMITATION = True
+
+  # Flag indicating whether the device has a limitation that does not allow it
+  # to execute Aware-based Ranging (whether direct or as part of discovery)
+  # for both Initiators and Responders. Only the first mode works.
+  RANGING_INITIATOR_RESPONDER_CONCURRENCY_LIMITATION = True
+
+  def __init__(self, controllers):
+    AwareBaseTest.__init__(self, controllers)
+    RttBaseTest.__init__(self, controllers)
+
+  def setup_test(self):
+    """Manual setup here due to multiple inheritance: explicitly execute the
+    setup method from both parents."""
+    AwareBaseTest.setup_test(self)
+    RttBaseTest.setup_test(self)
+
+  def teardown_test(self):
+    """Manual teardown here due to multiple inheritance: explicitly execute the
+    teardown method from both parents."""
+    AwareBaseTest.teardown_test(self)
+    RttBaseTest.teardown_test(self)
+
+  #########################################################################
+
+  def run_discovery(self, p_config, s_config, expect_discovery,
+      expect_range=False):
+    """Run discovery on the 2 input devices with the specified configurations.
+
+    Args:
+      p_config, s_config: Publisher and Subscriber discovery configuration.
+      expect_discovery: True or False indicating whether discovery is expected
+                        with the specified configurations.
+      expect_range: True if we expect distance results (i.e. ranging to happen).
+                    Only relevant if expect_discovery is True.
+    Returns:
+      p_dut, s_dut: Publisher/Subscribe DUT
+      p_disc_id, s_disc_id: Publisher/Subscribe discovery session ID
+    """
+    p_dut = self.android_devices[0]
+    p_dut.pretty_name = "Publisher"
+    s_dut = self.android_devices[1]
+    s_dut.pretty_name = "Subscriber"
+
+    # Publisher+Subscriber: attach and wait for confirmation
+    p_id = p_dut.droid.wifiAwareAttach(False)
+    autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+    time.sleep(self.device_startup_offset)
+    s_id = s_dut.droid.wifiAwareAttach(False)
+    autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+    # Publisher: start publish and wait for confirmation
+    p_disc_id = p_dut.droid.wifiAwarePublish(p_id, p_config)
+    autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+    # Subscriber: start subscribe and wait for confirmation
+    s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id, s_config)
+    autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+    # Subscriber: wait or fail on service discovery
+    if expect_discovery:
+      event = autils.wait_for_event(s_dut,
+                                    aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+      if expect_range:
+        asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                            "Discovery with ranging expected!")
+      else:
+        asserts.assert_false(
+          aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+          "Discovery with ranging NOT expected!")
+    else:
+      autils.fail_on_event(s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+    # (single) sleep for timeout period and then verify that no further events
+    time.sleep(autils.EVENT_TIMEOUT)
+    autils.verify_no_more_events(p_dut, timeout=0)
+    autils.verify_no_more_events(s_dut, timeout=0)
+
+    return p_dut, s_dut, p_disc_id, s_disc_id
+
+  def run_discovery_update(self, p_dut, s_dut, p_disc_id, s_disc_id, p_config,
+      s_config, expect_discovery, expect_range=False):
+    """Run discovery on the 2 input devices with the specified update
+    configurations. I.e. update the existing discovery sessions with the
+    configurations.
+
+    Args:
+      p_dut, s_dut: Publisher/Subscriber DUTs.
+      p_disc_id, s_disc_id: Publisher/Subscriber discovery session IDs.
+      p_config, s_config: Publisher and Subscriber discovery configuration.
+      expect_discovery: True or False indicating whether discovery is expected
+                        with the specified configurations.
+      expect_range: True if we expect distance results (i.e. ranging to happen).
+                    Only relevant if expect_discovery is True.
+    """
+
+    # try to perform reconfiguration at same time (and wait once for all
+    # confirmations)
+    if p_config is not None:
+      p_dut.droid.wifiAwareUpdatePublish(p_disc_id, p_config)
+    if s_config is not None:
+      s_dut.droid.wifiAwareUpdateSubscribe(s_disc_id, s_config)
+
+    if p_config is not None:
+      autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED)
+    if s_config is not None:
+      autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED)
+
+    # Subscriber: wait or fail on service discovery
+    if expect_discovery:
+      event = autils.wait_for_event(s_dut,
+                                    aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+      if expect_range:
+        asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                            "Discovery with ranging expected!")
+      else:
+        asserts.assert_false(
+            aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+            "Discovery with ranging NOT expected!")
+    else:
+      autils.fail_on_event(s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+    # (single) sleep for timeout period and then verify that no further events
+    time.sleep(autils.EVENT_TIMEOUT)
+    autils.verify_no_more_events(p_dut, timeout=0)
+    autils.verify_no_more_events(s_dut, timeout=0)
+
+  def run_discovery_prange_sminmax_outofrange(self, is_unsolicited_passive):
+    """Run discovery with ranging:
+    - Publisher enables ranging
+    - Subscriber enables ranging with min/max such that out of range (min=large,
+      max=large+1)
+
+    Expected: no discovery
+
+    This is a baseline test for the update-configuration tests.
+
+    Args:
+      is_unsolicited_passive: True for Unsolicited/Passive, False for
+                              Solicited/Active.
+    Returns: the return arguments of the run_discovery.
+    """
+    pub_type = (aconsts.PUBLISH_TYPE_UNSOLICITED if is_unsolicited_passive
+                else aconsts.PUBLISH_TYPE_SOLICITED)
+    sub_type = (aconsts.SUBSCRIBE_TYPE_PASSIVE if is_unsolicited_passive
+                else aconsts.SUBSCRIBE_TYPE_ACTIVE)
+    return self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME, pub_type,
+                                           ssi=self.getname(2)),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME, sub_type,
+                                           ssi=self.getname(2)),
+            min_distance_mm=1000000,
+            max_distance_mm=1000001),
+        expect_discovery=False)
+
+  def getname(self, level=1):
+    """Python magic to return the name of the *calling* function.
+
+    Args:
+      level: How many levels up to go for the method name. Default = calling
+             method.
+    """
+    return sys._getframe(level).f_code.co_name
+
+  #########################################################################
+  # Run discovery with ranging configuration.
+  #
+  # Names: test_ranged_discovery_<ptype>_<stype>_<p_range>_<s_range>_<ref_dist>
+  #
+  # where:
+  # <ptype>_<stype>: unsolicited_passive or solicited_active
+  # <p_range>: prange or pnorange
+  # <s_range>: smin or smax or sminmax or snorange
+  # <ref_distance>: inrange or outoforange
+  #########################################################################
+
+  @test_tracker_info(uuid="3a216e9a-7a57-4741-89c0-84456975e1ac")
+  def test_ranged_discovery_unsolicited_passive_prange_snorange(self):
+    """Verify discovery with ranging:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber disables ranging
+
+    Expect: normal discovery (as if no ranging performed) - no distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.create_discovery_config(self.SERVICE_NAME,
+                                                aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                                ssi=self.getname()),
+        expect_discovery=True,
+        expect_range=False)
+
+  @test_tracker_info(uuid="859a321e-18e2-437b-aa7a-2a45a42ee737")
+  def test_ranged_discovery_solicited_active_prange_snorange(self):
+    """Verify discovery with ranging:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber disables ranging
+
+    Expect: normal discovery (as if no ranging performed) - no distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_SOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.create_discovery_config(self.SERVICE_NAME,
+                                                aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                                ssi=self.getname()),
+        expect_discovery=True,
+        expect_range=False)
+
+  @test_tracker_info(uuid="12a4f899-4f70-4641-8f3c-351004669b71")
+  def test_ranged_discovery_unsolicited_passive_pnorange_smax_inrange(self):
+    """Verify discovery with ranging:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher disables ranging
+    - Subscriber enables ranging with max such that always within range (large
+      max)
+
+    Expect: normal discovery (as if no ranging performed) - no distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=False),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=None,
+            max_distance_mm=1000000),
+        expect_discovery=True,
+        expect_range=False)
+
+  @test_tracker_info(uuid="b7f90793-113d-4355-be20-856d92ac939f")
+  def test_ranged_discovery_solicited_active_pnorange_smax_inrange(self):
+    """Verify discovery with ranging:
+    - Solicited Publish/Active Subscribe
+    - Publisher disables ranging
+    - Subscriber enables ranging with max such that always within range (large
+      max)
+
+    Expect: normal discovery (as if no ranging performed) - no distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_SOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=False),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=None,
+            max_distance_mm=1000000),
+        expect_discovery=True,
+        expect_range=False)
+
+  @test_tracker_info(uuid="da3ab6df-58f9-44ae-b7be-8200d9e1bb76")
+  def test_ranged_discovery_unsolicited_passive_pnorange_smin_outofrange(self):
+    """Verify discovery with ranging:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher disables ranging
+    - Subscriber enables ranging with min such that always out of range (large
+      min)
+
+    Expect: normal discovery (as if no ranging performed) - no distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=False),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=1000000,
+            max_distance_mm=None),
+        expect_discovery=True,
+        expect_range=False)
+
+  @test_tracker_info(uuid="275e0806-f266-4fa6-9ca0-1cfd7b65a6ca")
+  def test_ranged_discovery_solicited_active_pnorange_smin_outofrange(self):
+    """Verify discovery with ranging:
+    - Solicited Publish/Active Subscribe
+    - Publisher disables ranging
+    - Subscriber enables ranging with min such that always out of range (large
+      min)
+
+    Expect: normal discovery (as if no ranging performed) - no distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_SOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=False),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=1000000,
+            max_distance_mm=None),
+        expect_discovery=True,
+        expect_range=False)
+
+  @test_tracker_info(uuid="8cd0aa1e-6866-4a5d-a550-f25483eebea1")
+  def test_ranged_discovery_unsolicited_passive_prange_smin_inrange(self):
+    """Verify discovery with ranging:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with min such that in range (min=0)
+
+    Expect: discovery with distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=0,
+            max_distance_mm=None),
+        expect_discovery=True,
+        expect_range=True)
+
+  @test_tracker_info(uuid="97c22c54-669b-4f7a-bf51-2f484e5f3e74")
+  def test_ranged_discovery_unsolicited_passive_prange_smax_inrange(self):
+    """Verify discovery with ranging:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with max such that in range (max=large)
+
+    Expect: discovery with distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=None,
+            max_distance_mm=1000000),
+        expect_discovery=True,
+        expect_range=True)
+
+  @test_tracker_info(uuid="616673d7-9d0b-43de-a378-e5e949b51b32")
+  def test_ranged_discovery_unsolicited_passive_prange_sminmax_inrange(self):
+    """Verify discovery with ranging:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with min/max such that in range (min=0,
+      max=large)
+
+    Expect: discovery with distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=0,
+            max_distance_mm=1000000),
+        expect_discovery=True,
+        expect_range=True)
+
+  @test_tracker_info(uuid="2bf84912-dcad-4a8f-971f-e445a07f05ce")
+  def test_ranged_discovery_solicited_active_prange_smin_inrange(self):
+    """Verify discovery with ranging:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with min such that in range (min=0)
+
+    Expect: discovery with distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_SOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=0,
+            max_distance_mm=None),
+        expect_discovery=True,
+        expect_range=True)
+
+  @test_tracker_info(uuid="5cfd7961-9665-4742-a1b5-2d1fc97f9795")
+  def test_ranged_discovery_solicited_active_prange_smax_inrange(self):
+    """Verify discovery with ranging:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with max such that in range (max=large)
+
+    Expect: discovery with distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_SOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=None,
+            max_distance_mm=1000000),
+        expect_discovery=True,
+        expect_range=True)
+
+  @test_tracker_info(uuid="5cf650ad-0b42-4b7d-9e05-d5f45fe0554d")
+  def test_ranged_discovery_solicited_active_prange_sminmax_inrange(self):
+    """Verify discovery with ranging:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with min/max such that in range (min=0,
+      max=large)
+
+    Expect: discovery with distance
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_SOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=0,
+            max_distance_mm=1000000),
+        expect_discovery=True,
+        expect_range=True)
+
+  @test_tracker_info(uuid="5277f418-ac35-43ce-9b30-3c895272898e")
+  def test_ranged_discovery_unsolicited_passive_prange_smin_outofrange(self):
+    """Verify discovery with ranging:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with min such that out of range (min=large)
+
+    Expect: no discovery
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=1000000,
+            max_distance_mm=None),
+        expect_discovery=False)
+
+  @test_tracker_info(uuid="8a7e6ab1-acf4-41a7-a5fb-8c164d593b5f")
+  def test_ranged_discovery_unsolicited_passive_prange_smax_outofrange(self):
+    """Verify discovery with ranging:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with max such that in range (max=0)
+
+    Expect: no discovery
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=None,
+            max_distance_mm=0),
+        expect_discovery=False)
+
+  @test_tracker_info(uuid="b744f5f9-2641-4373-bf86-3752e2f9aace")
+  def test_ranged_discovery_unsolicited_passive_prange_sminmax_outofrange(self):
+    """Verify discovery with ranging:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with min/max such that out of range (min=large,
+      max=large+1)
+
+    Expect: no discovery
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=1000000,
+            max_distance_mm=1000001),
+        expect_discovery=False)
+
+  @test_tracker_info(uuid="d2e94199-b2e6-4fa5-a347-24594883c801")
+  def test_ranged_discovery_solicited_active_prange_smin_outofrange(self):
+    """Verify discovery with ranging:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with min such that out of range (min=large)
+
+    Expect: no discovery
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_SOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=1000000,
+            max_distance_mm=None),
+        expect_discovery=False)
+
+  @test_tracker_info(uuid="a5619835-496a-4244-a428-f85cba3d4115")
+  def test_ranged_discovery_solicited_active_prange_smax_outofrange(self):
+    """Verify discovery with ranging:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with max such that out of range (max=0)
+
+    Expect: no discovery
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_SOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=None,
+            max_distance_mm=0),
+        expect_discovery=False)
+
+  @test_tracker_info(uuid="12ebd91f-a973-410b-8ee1-0bd86024b921")
+  def test_ranged_discovery_solicited_active_prange_sminmax_outofrange(self):
+    """Verify discovery with ranging:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber enables ranging with min/max such that out of range (min=large,
+      max=large+1)
+
+    Expect: no discovery
+    """
+    self.run_discovery(
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_SOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=1000000,
+            max_distance_mm=1000001),
+        expect_discovery=False)
+
+  #########################################################################
+  # Run discovery with ranging configuration & update configurations after
+  # first run.
+  #
+  # Names: test_ranged_updated_discovery_<ptype>_<stype>_<scenario>
+  #
+  # where:
+  # <ptype>_<stype>: unsolicited_passive or solicited_active
+  # <scenario>: test scenario (details in name)
+  #########################################################################
+
+  @test_tracker_info(uuid="59442180-4a6c-428f-b926-86000e8339b4")
+  def test_ranged_updated_discovery_unsolicited_passive_oor_to_ir(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber:
+      - Starts: Ranging enabled, min/max such that out of range (min=large,
+                max=large+1)
+      - Reconfigured to: Ranging enabled, min/max such that in range (min=0,
+                        max=large)
+
+    Expect: discovery + ranging after update
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+        p_config=None, # no updates
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=0,
+            max_distance_mm=1000000),
+        expect_discovery=True,
+        expect_range=True)
+
+  @test_tracker_info(uuid="60188508-104d-42d5-ac3a-3605093c45d7")
+  def test_ranged_updated_discovery_unsolicited_passive_pub_unrange(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+                  max=large+1)
+    - Reconfigured to: Publisher disables ranging
+
+    Expect: discovery w/o ranging after update
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+        p_config=autils.create_discovery_config(self.SERVICE_NAME,
+                                             aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                             ssi=self.getname()),
+        s_config=None, # no updates
+        expect_discovery=True,
+        expect_range=False)
+
+  @test_tracker_info(uuid="f96b434e-751d-4eb5-ae01-0c5c3a6fb4a2")
+  def test_ranged_updated_discovery_unsolicited_passive_sub_unrange(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber:
+      - Starts: Ranging enabled, min/max such that out of range (min=large,
+                max=large+1)
+      - Reconfigured to: Ranging disabled
+
+    Expect: discovery w/o ranging after update
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+        p_config=None, # no updates
+        s_config=autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+        expect_discovery=True,
+        expect_range=False)
+
+  @test_tracker_info(uuid="78970de8-9362-4647-931a-3513bcf58e80")
+  def test_ranged_updated_discovery_unsolicited_passive_sub_oor(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber:
+      - Starts: Ranging enabled, min/max such that out of range (min=large,
+                max=large+1)
+      - Reconfigured to: different out-of-range setting
+
+    Expect: no discovery after update
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+        p_config=None, # no updates
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=100000,
+            max_distance_mm=100001),
+        expect_discovery=False)
+
+  @test_tracker_info(uuid="0841ad05-4899-4521-bd24-04a8e2e345ac")
+  def test_ranged_updated_discovery_unsolicited_passive_pub_same(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+                  max=large+1)
+    - Reconfigured to: Publisher with same settings (ranging enabled)
+
+    Expect: no discovery after update
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=None, # no updates
+        expect_discovery=False)
+
+  @test_tracker_info(uuid="ec6ca57b-f115-4516-813a-4572b930c8d3")
+  def test_ranged_updated_discovery_unsolicited_passive_multi_step(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+                  max=large+1)
+      - Expect: no discovery
+    - Reconfigured to: Ranging enabled, min/max such that in-range (min=0)
+      - Expect: discovery with ranging
+    - Reconfigured to: Ranging enabled, min/max such that out-of-range
+                       (min=large)
+      - Expect: no discovery
+    - Reconfigured to: Ranging disabled
+      - Expect: discovery without ranging
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+            p_config=None, # no updates
+            s_config=autils.add_ranging_to_sub(
+                autils.create_discovery_config(self.SERVICE_NAME,
+                                               aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                               ssi=self.getname()),
+                min_distance_mm=0,
+                max_distance_mm=None),
+            expect_discovery=True,
+            expect_range=True)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+            p_config=None, # no updates
+            s_config=autils.add_ranging_to_sub(
+                autils.create_discovery_config(self.SERVICE_NAME,
+                                               aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                               ssi=self.getname()),
+                min_distance_mm=1000000,
+                max_distance_mm=None),
+            expect_discovery=False)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+            p_config=None, # no updates
+            s_config=autils.create_discovery_config(self.SERVICE_NAME,
+                                               aconsts.SUBSCRIBE_TYPE_PASSIVE,
+                                               ssi=self.getname()),
+            expect_discovery=True,
+            expect_range=False)
+
+  @test_tracker_info(uuid="bbaac63b-000c-415f-bf19-0906f04031cd")
+  def test_ranged_updated_discovery_solicited_active_oor_to_ir(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber:
+      - Starts: Ranging enabled, min/max such that out of range (min=large,
+                max=large+1)
+      - Reconfigured to: Ranging enabled, min/max such that in range (min=0,
+                        max=large)
+
+    Expect: discovery + ranging after update
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+        p_config=None, # no updates
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=0,
+            max_distance_mm=1000000),
+        expect_discovery=True,
+        expect_range=True)
+
+  @test_tracker_info(uuid="c385b361-7955-4f34-9109-8d8ca81cb4cc")
+  def test_ranged_updated_discovery_solicited_active_pub_unrange(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+                  max=large+1)
+    - Reconfigured to: Publisher disables ranging
+
+    Expect: discovery w/o ranging after update
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+        p_config=autils.create_discovery_config(self.SERVICE_NAME,
+                                                 aconsts.PUBLISH_TYPE_SOLICITED,
+                                                 ssi=self.getname()),
+        s_config=None, # no updates
+        expect_discovery=True,
+        expect_range=False)
+
+  @test_tracker_info(uuid="ec5120ea-77ec-48c6-8820-48b82ad3dfd4")
+  def test_ranged_updated_discovery_solicited_active_sub_unrange(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber:
+      - Starts: Ranging enabled, min/max such that out of range (min=large,
+                max=large+1)
+      - Reconfigured to: Ranging disabled
+
+    Expect: discovery w/o ranging after update
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+        p_config=None, # no updates
+        s_config=autils.create_discovery_config(self.SERVICE_NAME,
+                                                 aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                                 ssi=self.getname()),
+        expect_discovery=True,
+        expect_range=False)
+
+  @test_tracker_info(uuid="6231cb42-91e4-48d3-b9db-b37efbe8537c")
+  def test_ranged_updated_discovery_solicited_active_sub_oor(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber:
+      - Starts: Ranging enabled, min/max such that out of range (min=large,
+                max=large+1)
+      - Reconfigured to: different out-of-range setting
+
+    Expect: no discovery after update
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+        p_config=None, # no updates
+        s_config=autils.add_ranging_to_sub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                           ssi=self.getname()),
+            min_distance_mm=100000,
+            max_distance_mm=100001),
+        expect_discovery=False)
+
+  @test_tracker_info(uuid="ec999420-6a50-455e-b624-f4c9b4cb7ea5")
+  def test_ranged_updated_discovery_solicited_active_pub_same(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Solicited Publish/Active Subscribe
+    - Publisher enables ranging
+    - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+                  max=large+1)
+    - Reconfigured to: Publisher with same settings (ranging enabled)
+
+    Expect: no discovery after update
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+        p_config=autils.add_ranging_to_pub(
+            autils.create_discovery_config(self.SERVICE_NAME,
+                                           aconsts.PUBLISH_TYPE_SOLICITED,
+                                           ssi=self.getname()),
+            enable_ranging=True),
+        s_config=None, # no updates
+        expect_discovery=False)
+
+  @test_tracker_info(uuid="ec6ca57b-f115-4516-813a-4572b930c8d3")
+  def test_ranged_updated_discovery_solicited_active_multi_step(self):
+    """Verify discovery with ranging operation with updated configuration:
+    - Unsolicited Publish/Passive Subscribe
+    - Publisher enables ranging
+    - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+                  max=large+1)
+      - Expect: no discovery
+    - Reconfigured to: Ranging enabled, min/max such that in-range (min=0)
+      - Expect: discovery with ranging
+    - Reconfigured to: Ranging enabled, min/max such that out-of-range
+                       (min=large)
+      - Expect: no discovery
+    - Reconfigured to: Ranging disabled
+      - Expect: discovery without ranging
+    """
+    (p_dut, s_dut, p_disc_id,
+     s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+            p_config=None, # no updates
+            s_config=autils.add_ranging_to_sub(
+                autils.create_discovery_config(self.SERVICE_NAME,
+                                               aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                               ssi=self.getname()),
+                min_distance_mm=0,
+                max_distance_mm=None),
+            expect_discovery=True,
+            expect_range=True)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+            p_config=None, # no updates
+            s_config=autils.add_ranging_to_sub(
+                autils.create_discovery_config(self.SERVICE_NAME,
+                                               aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                               ssi=self.getname()),
+                min_distance_mm=1000000,
+                max_distance_mm=None),
+            expect_discovery=False)
+    self.run_discovery_update(p_dut, s_dut, p_disc_id, s_disc_id,
+            p_config=None, # no updates
+            s_config=autils.create_discovery_config(self.SERVICE_NAME,
+                                                aconsts.SUBSCRIBE_TYPE_ACTIVE,
+                                                ssi=self.getname()),
+            expect_discovery=True,
+            expect_range=False)
+
+  #########################################################################
+
+  @test_tracker_info(uuid="6edc47ab-7300-4bff-b7dd-5de83f58928a")
+  def test_ranged_discovery_multi_session(self):
+    """Verify behavior with multiple concurrent discovery session with different
+    configurations:
+
+    Device A (Publisher):
+      Publisher AA: ranging enabled
+      Publisher BB: ranging enabled
+      Publisher CC: ranging enabled
+      Publisher DD: ranging disabled
+    Device B (Subscriber):
+      Subscriber AA: ranging out-of-range -> no match
+      Subscriber BB: ranging in-range -> match w/range
+      Subscriber CC: ranging disabled -> match w/o range
+      Subscriber DD: ranging out-of-range -> match w/o range
+    """
+    p_dut = self.android_devices[0]
+    p_dut.pretty_name = "Publisher"
+    s_dut = self.android_devices[1]
+    s_dut.pretty_name = "Subscriber"
+
+    # Publisher+Subscriber: attach and wait for confirmation
+    p_id = p_dut.droid.wifiAwareAttach(False)
+    autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+    time.sleep(self.device_startup_offset)
+    s_id = s_dut.droid.wifiAwareAttach(False)
+    autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+    # Subscriber: start sessions
+    aa_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+        s_id,
+        autils.add_ranging_to_sub(
+            autils.create_discovery_config("AA",
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE),
+            min_distance_mm=1000000, max_distance_mm=1000001),
+        True)
+    bb_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+        s_id,
+        autils.add_ranging_to_sub(
+            autils.create_discovery_config("BB",
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE),
+            min_distance_mm=0, max_distance_mm=1000000),
+        True)
+    cc_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+        s_id,
+        autils.create_discovery_config("CC", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+        True)
+    dd_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+        s_id,
+        autils.add_ranging_to_sub(
+            autils.create_discovery_config("DD",
+                                           aconsts.SUBSCRIBE_TYPE_PASSIVE),
+            min_distance_mm=1000000, max_distance_mm=1000001),
+        True)
+
+    autils.wait_for_event(s_dut, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, aa_s_disc_id))
+    autils.wait_for_event(s_dut, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, bb_s_disc_id))
+    autils.wait_for_event(s_dut, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, cc_s_disc_id))
+    autils.wait_for_event(s_dut, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, dd_s_disc_id))
+
+    # Publisher: start sessions
+    aa_p_disc_id = p_dut.droid.wifiAwarePublish(
+        p_id,
+        autils.add_ranging_to_pub(
+            autils.create_discovery_config("AA",
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED),
+            enable_ranging=True),
+        True)
+    bb_p_disc_id = p_dut.droid.wifiAwarePublish(
+        p_id,
+        autils.add_ranging_to_pub(
+            autils.create_discovery_config("BB",
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED),
+            enable_ranging=True),
+        True)
+    cc_p_disc_id = p_dut.droid.wifiAwarePublish(
+        p_id,
+        autils.add_ranging_to_pub(
+            autils.create_discovery_config("CC",
+                                           aconsts.PUBLISH_TYPE_UNSOLICITED),
+            enable_ranging=True),
+        True)
+    dd_p_disc_id = p_dut.droid.wifiAwarePublish(
+        p_id,
+        autils.create_discovery_config("DD", aconsts.PUBLISH_TYPE_UNSOLICITED),
+        True)
+
+    autils.wait_for_event(p_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, aa_p_disc_id))
+    autils.wait_for_event(p_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, bb_p_disc_id))
+    autils.wait_for_event(p_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, cc_p_disc_id))
+    autils.wait_for_event(p_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, dd_p_disc_id))
+
+    # Expected and unexpected service discovery
+    event = autils.wait_for_event(s_dut, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, bb_s_disc_id))
+    asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                        "Discovery with ranging for BB expected!")
+    event = autils.wait_for_event(s_dut, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, cc_s_disc_id))
+    asserts.assert_false(
+        aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+        "Discovery with ranging for CC NOT expected!")
+    event = autils.wait_for_event(s_dut, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, dd_s_disc_id))
+    asserts.assert_false(
+        aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+        "Discovery with ranging for DD NOT expected!")
+    autils.fail_on_event(s_dut, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, aa_s_disc_id))
+
+    # (single) sleep for timeout period and then verify that no further events
+    time.sleep(autils.EVENT_TIMEOUT)
+    autils.verify_no_more_events(p_dut, timeout=0)
+    autils.verify_no_more_events(s_dut, timeout=0)
+
+  #########################################################################
+
+  @test_tracker_info(uuid="deede47f-a54c-46d9-88bb-f4482fbd8470")
+  def test_ndp_concurrency(self):
+    """Verify the behavior of Wi-Fi Aware Ranging whenever an NDP is created -
+    for those devices that have a concurrency limitation that does not allow
+    Aware Ranging, whether direct or as part of discovery.
+
+    Publisher: start 3 services
+      AA w/o ranging
+      BB w/ ranging
+      CC w/ ranging
+      DD w/ ranging
+    Subscriber: start 2 services
+      AA w/o ranging
+      BB w/ ranging out-of-range
+      (do not start CC!)
+      DD w/ ranging in-range
+    Expect AA discovery, DD discovery w/range, but no BB
+    Start NDP in context of AA
+    IF NDP_CONCURRENCY_LIMITATION:
+      Verify discovery on BB w/o range
+    Start EE w/ranging out-of-range
+    Start FF w/ranging in-range
+    IF NDP_CONCURRENCY_LIMITATION:
+      Verify discovery on EE w/o range
+      Verify discovery on FF w/o range
+    Else:
+      Verify discovery on FF w/ range
+    Tear down NDP
+    Subscriber
+      Start CC w/ ranging out-of-range
+      Wait to verify that do not get match
+      Update configuration to be in-range
+      Verify that get match with ranging information
+    """
+    p_dut = self.android_devices[0]
+    p_dut.pretty_name = "Publisher"
+    s_dut = self.android_devices[1]
+    s_dut.pretty_name = "Subscriber"
+
+    # Publisher+Subscriber: attach and wait for confirmation
+    p_id = p_dut.droid.wifiAwareAttach(False)
+    autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+    time.sleep(self.device_startup_offset)
+    s_id = s_dut.droid.wifiAwareAttach(False)
+    autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+    # Publisher: AA w/o ranging, BB w/ ranging, CC w/ ranging, DD w/ ranging
+    aa_p_id = p_dut.droid.wifiAwarePublish(p_id,
+        autils.create_discovery_config("AA", aconsts.PUBLISH_TYPE_SOLICITED),
+                                           True)
+    autils.wait_for_event(p_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, aa_p_id))
+    bb_p_id = p_dut.droid.wifiAwarePublish(p_id, autils.add_ranging_to_pub(
+        autils.create_discovery_config("BB", aconsts.PUBLISH_TYPE_UNSOLICITED),
+        enable_ranging=True), True)
+    autils.wait_for_event(p_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, bb_p_id))
+    cc_p_id = p_dut.droid.wifiAwarePublish(p_id, autils.add_ranging_to_pub(
+      autils.create_discovery_config("CC", aconsts.PUBLISH_TYPE_UNSOLICITED),
+      enable_ranging=True), True)
+    autils.wait_for_event(p_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, cc_p_id))
+    dd_p_id = p_dut.droid.wifiAwarePublish(p_id, autils.add_ranging_to_pub(
+        autils.create_discovery_config("DD", aconsts.PUBLISH_TYPE_UNSOLICITED),
+        enable_ranging=True), True)
+    autils.wait_for_event(p_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, dd_p_id))
+
+    # Subscriber: AA w/o ranging, BB w/ranging out-of-range,
+    #             DD w /ranging in-range
+    aa_s_id = s_dut.droid.wifiAwareSubscribe(s_id,
+        autils.create_discovery_config("AA", aconsts.SUBSCRIBE_TYPE_ACTIVE),
+                                             True)
+    autils.wait_for_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, aa_s_id))
+    bb_s_id = s_dut.droid.wifiAwareSubscribe(s_id, autils.add_ranging_to_sub(
+      autils.create_discovery_config("BB", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+      min_distance_mm=1000000, max_distance_mm=1000001), True)
+    autils.wait_for_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, bb_s_id))
+    dd_s_id = s_dut.droid.wifiAwareSubscribe(s_id, autils.add_ranging_to_sub(
+        autils.create_discovery_config("DD", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+        min_distance_mm=None, max_distance_mm=1000000), True)
+    autils.wait_for_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, dd_s_id))
+
+    # verify: AA discovered, BB not discovered, DD discovery w/range
+    event = autils.wait_for_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, aa_s_id))
+    asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                         "Discovery with ranging for AA NOT expected!")
+    aa_peer_id_on_sub = event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+    autils.fail_on_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, bb_s_id))
+    event = autils.wait_for_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, dd_s_id))
+    asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                        "Discovery with ranging for DD expected!")
+
+    # start NDP in context of AA:
+
+    # Publisher: request network (from ANY)
+    p_req_key = autils.request_network(p_dut,
+        p_dut.droid.wifiAwareCreateNetworkSpecifier(aa_p_id, None))
+
+    # Subscriber: request network
+    s_req_key = autils.request_network(s_dut,
+        s_dut.droid.wifiAwareCreateNetworkSpecifier(aa_s_id, aa_peer_id_on_sub))
+
+    # Publisher & Subscriber: wait for network formation
+    p_net_event = autils.wait_for_event_with_keys(p_dut,
+                                    cconsts.EVENT_NETWORK_CALLBACK,
+                                    autils.EVENT_TIMEOUT, (
+                                    cconsts.NETWORK_CB_KEY_EVENT,
+                                    cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+                                    (cconsts.NETWORK_CB_KEY_ID,
+                                     p_req_key))
+    s_net_event = autils.wait_for_event_with_keys(s_dut,
+                                    cconsts.EVENT_NETWORK_CALLBACK,
+                                    autils.EVENT_TIMEOUT, (
+                                    cconsts.NETWORK_CB_KEY_EVENT,
+                                    cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+                                    (cconsts.NETWORK_CB_KEY_ID,
+                                     s_req_key))
+
+    p_aware_if = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+    s_aware_if = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+    p_ipv6 = p_dut.droid.connectivityGetLinkLocalIpv6Address(p_aware_if).split(
+        "%")[0]
+    s_ipv6 = s_dut.droid.connectivityGetLinkLocalIpv6Address(s_aware_if).split(
+        "%")[0]
+
+    self.log.info("AA NDP Interface names: P=%s, S=%s", p_aware_if, s_aware_if)
+    self.log.info("AA NDP Interface addresses (IPv6): P=%s, S=%s", p_ipv6,
+                  s_ipv6)
+
+    if self.RANGING_NDP_CONCURRENCY_LIMITATION:
+      # Expect BB to now discover w/o ranging
+      event = autils.wait_for_event(s_dut, autils.decorate_event(
+          aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, bb_s_id))
+      asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                           "Discovery with ranging for BB NOT expected!")
+
+    # Publishers: EE, FF w/ ranging
+    ee_p_id = p_dut.droid.wifiAwarePublish(p_id, autils.add_ranging_to_pub(
+        autils.create_discovery_config("EE", aconsts.PUBLISH_TYPE_SOLICITED),
+        enable_ranging=True), True)
+    autils.wait_for_event(p_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, ee_p_id))
+    ff_p_id = p_dut.droid.wifiAwarePublish(p_id, autils.add_ranging_to_pub(
+        autils.create_discovery_config("FF", aconsts.PUBLISH_TYPE_UNSOLICITED),
+        enable_ranging=True), True)
+    autils.wait_for_event(p_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, ff_p_id))
+
+    # Subscribers: EE out-of-range, FF in-range
+    ee_s_id = s_dut.droid.wifiAwareSubscribe(s_id, autils.add_ranging_to_sub(
+        autils.create_discovery_config("EE", aconsts.SUBSCRIBE_TYPE_ACTIVE),
+        min_distance_mm=1000000, max_distance_mm=1000001), True)
+    autils.wait_for_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, ee_s_id))
+    ff_s_id = s_dut.droid.wifiAwareSubscribe(s_id, autils.add_ranging_to_sub(
+        autils.create_discovery_config("FF", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+        min_distance_mm=None, max_distance_mm=1000000), True)
+    autils.wait_for_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, ff_s_id))
+
+    if self.RANGING_NDP_CONCURRENCY_LIMITATION:
+      # Expect EE & FF discovery w/o range
+      event = autils.wait_for_event(s_dut, autils.decorate_event(
+          aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, ee_s_id))
+      asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                           "Discovery with ranging for EE NOT expected!")
+      event = autils.wait_for_event(s_dut, autils.decorate_event(
+          aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, ff_s_id))
+      asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                           "Discovery with ranging for FF NOT expected!")
+    else:
+      event = autils.wait_for_event(s_dut, autils.decorate_event(
+          aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, ff_s_id))
+      asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                           "Discovery with ranging for FF expected!")
+
+    # tear down NDP
+    p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+    s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+
+    time.sleep(5) # give time for NDP termination to finish
+
+    # Subscriber: start CC out-of-range - no discovery expected!
+    cc_s_id = s_dut.droid.wifiAwareSubscribe(s_id, autils.add_ranging_to_sub(
+        autils.create_discovery_config("CC", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+        min_distance_mm=1000000, max_distance_mm=1000001), True)
+    autils.wait_for_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, cc_s_id))
+    autils.fail_on_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, cc_s_id))
+
+    # Subscriber: modify CC to in-range - expect discovery w/ range
+    s_dut.droid.wifiAwareUpdateSubscribe(cc_s_id, autils.add_ranging_to_sub(
+        autils.create_discovery_config("CC", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+        min_distance_mm=None, max_distance_mm=1000001))
+    autils.wait_for_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED, cc_s_id))
+    event = autils.wait_for_event(s_dut, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, cc_s_id))
+    asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                        "Discovery with ranging for CC expected!")
+
+  @test_tracker_info(uuid="d94dac91-4090-4c03-a867-6dfac6558ba3")
+  def test_role_concurrency(self):
+    """Verify the behavior of Wi-Fi Aware Ranging (in the context of discovery)
+     when the device has concurrency limitations which do not permit concurrent
+     Initiator and Responder roles on the same device. In such case it is
+     expected that normal discovery without ranging is executed AND that ranging
+     is restored whenever the concurrency constraints are removed.
+
+     Note: all Subscribers are in-range.
+
+     DUT1: start multiple services
+      Publish AA w/ ranging (unsolicited)
+      Subscribe BB w/ ranging (active)
+      Publish CC w/ ranging (unsolicited)
+      Publish DD w/o ranging (solicited)
+      Subscribe EE w/ ranging (passive)
+      Subscribe FF w/ ranging (active)
+     DUT2: start multiple services
+      Subscribe AA w/ ranging (passive)
+      Publish BB w/ ranging (solicited)
+      Subscribe DD w/o ranging (active)
+     Expect
+      DUT2: AA match w/ range information
+      DUT1: BB match w/o range information (concurrency disables ranging)
+      DUT2: DD match w/o range information
+     DUT1: Terminate AA
+     DUT2:
+      Terminate AA
+      Start Publish EE w/ ranging (unsolicited)
+     DUT1: expect EE w/o ranging
+     DUT1: Terminate CC
+     DUT2: Start Publish FF w/ ranging (solicited)
+     DUT1: expect FF w/ ranging information - should finally be back up
+     """
+    dut1 = self.android_devices[0]
+    dut1.pretty_name = "DUT1"
+    dut2 = self.android_devices[1]
+    dut2.pretty_name = "DUT2"
+
+    # Publisher+Subscriber: attach and wait for confirmation
+    dut1_id = dut1.droid.wifiAwareAttach(False)
+    autils.wait_for_event(dut1, aconsts.EVENT_CB_ON_ATTACHED)
+    time.sleep(self.device_startup_offset)
+    dut2_id = dut2.droid.wifiAwareAttach(False)
+    autils.wait_for_event(dut2, aconsts.EVENT_CB_ON_ATTACHED)
+
+    # DUT1: initial service bringup
+    aa_p_id = dut1.droid.wifiAwarePublish(dut1_id, autils.add_ranging_to_pub(
+        autils.create_discovery_config("AA", aconsts.PUBLISH_TYPE_UNSOLICITED),
+        enable_ranging=True), True)
+    autils.wait_for_event(dut1, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, aa_p_id))
+    bb_s_id = dut1.droid.wifiAwareSubscribe(dut1_id, autils.add_ranging_to_sub(
+        autils.create_discovery_config("BB", aconsts.SUBSCRIBE_TYPE_ACTIVE),
+        min_distance_mm=None, max_distance_mm=1000000), True)
+    autils.wait_for_event(dut1, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, bb_s_id))
+    cc_p_id = dut1.droid.wifiAwarePublish(dut1_id, autils.add_ranging_to_pub(
+        autils.create_discovery_config("CC", aconsts.PUBLISH_TYPE_UNSOLICITED),
+        enable_ranging=True), True)
+    autils.wait_for_event(dut1, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, cc_p_id))
+    dd_p_id = dut1.droid.wifiAwarePublish(dut1_id,
+      autils.create_discovery_config("DD", aconsts.PUBLISH_TYPE_SOLICITED),
+                                           True)
+    autils.wait_for_event(dut1, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, dd_p_id))
+    ee_s_id = dut1.droid.wifiAwareSubscribe(dut1_id, autils.add_ranging_to_sub(
+        autils.create_discovery_config("EE", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+        min_distance_mm=None, max_distance_mm=1000000), True)
+    autils.wait_for_event(dut1, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, ee_s_id))
+    ff_s_id = dut1.droid.wifiAwareSubscribe(dut1_id, autils.add_ranging_to_sub(
+        autils.create_discovery_config("FF", aconsts.SUBSCRIBE_TYPE_ACTIVE),
+        min_distance_mm=None, max_distance_mm=1000000), True)
+    autils.wait_for_event(dut1, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, ff_s_id))
+
+    # DUT2: initial service bringup
+    aa_s_id = dut2.droid.wifiAwareSubscribe(dut2_id, autils.add_ranging_to_sub(
+        autils.create_discovery_config("AA", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+        min_distance_mm=None, max_distance_mm=1000000), True)
+    autils.wait_for_event(dut2, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, aa_s_id))
+    bb_p_id = dut2.droid.wifiAwarePublish(dut2_id, autils.add_ranging_to_pub(
+        autils.create_discovery_config("BB", aconsts.PUBLISH_TYPE_SOLICITED),
+        enable_ranging=True), True)
+    autils.wait_for_event(dut2, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, bb_p_id))
+    dd_s_id = dut2.droid.wifiAwareSubscribe(dut2_id,
+        autils.create_discovery_config("AA", aconsts.SUBSCRIBE_TYPE_ACTIVE),
+        True)
+    autils.wait_for_event(dut2, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, dd_s_id))
+
+    # Initial set of discovery events for AA, BB, and DD (which are up)
+    event = autils.wait_for_event(dut2, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, aa_s_id))
+    asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                        "Discovery with ranging for AA expected!")
+    event = autils.wait_for_event(dut1, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, bb_s_id))
+    if self.RANGING_INITIATOR_RESPONDER_CONCURRENCY_LIMITATION:
+      asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                           "Discovery with ranging for BB NOT expected!")
+    else:
+      asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                           "Discovery with ranging for BB expected!")
+    event = autils.wait_for_event(dut2, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, dd_s_id))
+    asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                         "Discovery with ranging for DD NOT expected!")
+
+    # DUT1/DUT2: terminate AA
+    dut1.droid.wifiAwareDestroyDiscoverySession(aa_p_id)
+    dut2.droid.wifiAwareDestroyDiscoverySession(aa_s_id)
+
+    time.sleep(5) # guarantee that session terminated (and host recovered?)
+
+    # DUT2: try EE service - ranging still disabled
+    ee_p_id = dut2.droid.wifiAwarePublish(dut2_id, autils.add_ranging_to_pub(
+        autils.create_discovery_config("EE", aconsts.PUBLISH_TYPE_UNSOLICITED),
+        enable_ranging=True), True)
+    autils.wait_for_event(dut2, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, ee_p_id))
+
+    event = autils.wait_for_event(dut1, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, ee_s_id))
+    if self.RANGING_INITIATOR_RESPONDER_CONCURRENCY_LIMITATION:
+      asserts.assert_false(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                           "Discovery with ranging for EE NOT expected!")
+    else:
+      asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                          "Discovery with ranging for EE expected!")
+
+    # DUT1: terminate CC - last publish w/ ranging on DUT!
+    dut1.droid.wifiAwareDestroyDiscoverySession(cc_p_id)
+
+    time.sleep(5) # guarantee that session terminated (and host recovered?)
+
+    # DUT2: try FF service - ranging should now function
+    ff_p_id = dut2.droid.wifiAwarePublish(dut2_id, autils.add_ranging_to_pub(
+        autils.create_discovery_config("FF", aconsts.PUBLISH_TYPE_SOLICITED),
+        enable_ranging=True), True)
+    autils.wait_for_event(dut2, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, ff_p_id))
+
+    event = autils.wait_for_event(dut1, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, ff_s_id))
+    asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                        "Discovery with ranging for FF expected!")
+
+
+  @test_tracker_info(uuid="6700eab8-a172-43cd-aed3-e6577ce8fd89")
+  def test_discovery_direct_concurrency(self):
+    """Verify the behavior of Wi-Fi Aware Ranging used as part of discovery and
+    as direct ranging to a peer device.
+
+    Process:
+    - Start YYY service with ranging in-range
+    - Start XXX service with ranging out-of-range
+    - Start performing direct Ranging
+    - While above going on update XXX to be in-range
+    - Keep performing direct Ranging in context of YYY
+    - Stop direct Ranging and look for XXX to discover
+    """
+    dut1 = self.android_devices[0]
+    dut1.pretty_name = "DUT1"
+    dut2 = self.android_devices[1]
+    dut2.pretty_name = "DUT2"
+
+    # DUTs: attach and wait for confirmation
+    dut1_id = dut1.droid.wifiAwareAttach(False)
+    autils.wait_for_event(dut1, aconsts.EVENT_CB_ON_ATTACHED)
+    time.sleep(self.device_startup_offset)
+    dut2_id = dut2.droid.wifiAwareAttach(True)
+    event = autils.wait_for_event(dut2, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+    dut2_mac = event['data']['mac']
+
+    # DUT1: publishers bring-up
+    xxx_p_id = dut1.droid.wifiAwarePublish(dut1_id, autils.add_ranging_to_pub(
+      autils.create_discovery_config("XXX", aconsts.PUBLISH_TYPE_UNSOLICITED),
+      enable_ranging=True), True)
+    autils.wait_for_event(dut1, autils.decorate_event(
+      aconsts.SESSION_CB_ON_PUBLISH_STARTED, xxx_p_id))
+    yyy_p_id = dut1.droid.wifiAwarePublish(dut1_id, autils.add_ranging_to_pub(
+        autils.create_discovery_config("YYY", aconsts.PUBLISH_TYPE_UNSOLICITED),
+        enable_ranging=True), True)
+    autils.wait_for_event(dut1, autils.decorate_event(
+        aconsts.SESSION_CB_ON_PUBLISH_STARTED, yyy_p_id))
+
+    # DUT2: subscribers bring-up
+    xxx_s_id = dut2.droid.wifiAwareSubscribe(dut2_id, autils.add_ranging_to_sub(
+      autils.create_discovery_config("XXX", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+      min_distance_mm=1000000, max_distance_mm=1000001), True)
+    autils.wait_for_event(dut2, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, xxx_s_id))
+    yyy_s_id = dut2.droid.wifiAwareSubscribe(dut2_id, autils.add_ranging_to_sub(
+        autils.create_discovery_config("YYY", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+        min_distance_mm=None, max_distance_mm=1000000), True)
+    autils.wait_for_event(dut2, autils.decorate_event(
+        aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, yyy_s_id))
+
+    # Service discovery: YYY (with range info), but no XXX
+    event = autils.wait_for_event(dut2, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, yyy_s_id))
+    asserts.assert_true(aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+                        "Discovery with ranging for YYY expected!")
+    yyy_peer_id_on_sub = event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+
+    autils.fail_on_event(dut2, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, xxx_s_id))
+
+    # Direct ranging
+    results21 = []
+    for iter in range(10):
+      id = dut2.droid.wifiRttStartRangingToAwarePeerId(yyy_peer_id_on_sub)
+      event = autils.wait_for_event(dut2, rutils.decorate_event(
+        rconsts.EVENT_CB_RANGING_ON_RESULT, id))
+      results21.append(event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0])
+
+    time.sleep(5) # while switching roles
+
+    results12 = []
+    for iter in range(10):
+      id = dut1.droid.wifiRttStartRangingToAwarePeerMac(dut2_mac)
+      event = autils.wait_for_event(dut1, rutils.decorate_event(
+        rconsts.EVENT_CB_RANGING_ON_RESULT, id))
+      results12.append(event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0])
+
+    stats = [rutils.extract_stats(results12, 0, 0, 0),
+             rutils.extract_stats(results21, 0, 0, 0)]
+
+    # Update XXX to be within range
+    dut2.droid.wifiAwareUpdateSubscribe(xxx_s_id, autils.add_ranging_to_sub(
+      autils.create_discovery_config("XXX", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+      min_distance_mm=None, max_distance_mm=1000000))
+    autils.wait_for_event(dut2, autils.decorate_event(
+      aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED, xxx_s_id))
+
+    # Expect discovery on XXX - wait until discovery with ranging:
+    # - 0 or more: without ranging info (due to concurrency limitations)
+    # - 1 or more: with ranging (once concurrency limitation relieved)
+    num_events = 0
+    while True:
+      event = autils.wait_for_event(dut2, autils.decorate_event(
+          aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, xxx_s_id))
+      if aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"]:
+        break
+      num_events = num_events + 1
+      asserts.assert_true(num_events < 10, # arbitrary safety valve
+                          "Way too many discovery events without ranging!")
+
+    asserts.explicit_pass("Discovery/Direct RTT Concurrency Pass", extras=stats)
\ No newline at end of file
diff --git a/acts/tests/google/wifi/rtt/functional/RangeApMiscTest.py b/acts/tests/google/wifi/rtt/functional/RangeApMiscTest.py
new file mode 100644
index 0000000..dd5560d
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeApMiscTest.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts import asserts
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeApMiscTest(RttBaseTest):
+  """Test class for RTT ranging to Access Points - miscellaneous tests which
+  do not fit into the strict IEEE 802.11mc supporting or non-supporting test
+  beds - e.g. a mixed test."""
+
+  # Number of RTT iterations
+  NUM_ITER = 10
+
+  # Time gap (in seconds) between iterations
+  TIME_BETWEEN_ITERATIONS = 0
+
+  def __init__(self, controllers):
+    RttBaseTest.__init__(self, controllers)
+
+  #############################################################################
+
+  def test_rtt_mixed_80211mc_supporting_aps_wo_privilege(self):
+    """Scan for APs and perform RTT on one supporting and one non-supporting
+    IEEE 802.11mc APs with the device not having privilege access (expect
+    failures)."""
+    dut = self.android_devices[0]
+    rutils.config_privilege_override(dut, True)
+    rtt_aps = rutils.scan_with_rtt_support_constraint(dut, True)
+    non_rtt_aps = rutils.scan_with_rtt_support_constraint(dut, False)
+    mix_list = [rtt_aps[0], non_rtt_aps[0]]
+    dut.log.debug("Visible non-IEEE 802.11mc APs=%s", mix_list)
+    events = rutils.run_ranging(dut, mix_list, self.NUM_ITER,
+                                self.TIME_BETWEEN_ITERATIONS)
+    stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+                                   self.rtt_reference_distance_margin_mm,
+                                   self.rtt_min_expected_rssi_dbm,
+                                   self.lci_reference, self.lcr_reference)
+    dut.log.debug("Stats=%s", stats)
+
+    for bssid, stat in stats.items():
+      asserts.assert_true(stat['num_no_results'] == 0,
+                          "Missing (timed-out) results", extras=stats)
+      if bssid == rtt_aps[0][wutils.WifiEnums.BSSID_KEY]:
+        asserts.assert_false(stat['any_lci_mismatch'],
+                             "LCI mismatch", extras=stats)
+        asserts.assert_false(stat['any_lcr_mismatch'],
+                             "LCR mismatch", extras=stats)
+        asserts.assert_equal(stat['num_invalid_rssi'], 0, "Invalid RSSI",
+                            extras=stats)
+        asserts.assert_true(stat['num_failures'] <=
+                            self.rtt_max_failure_rate_two_sided_rtt_percentage
+                            * stat['num_results'] / 100,
+                            "Failure rate is too high", extras=stats)
+        asserts.assert_true(stat['num_range_out_of_margin'] <=
+                    self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+                    * stat['num_success_results'] / 100,
+                    "Results exceeding error margin rate is too high",
+                    extras=stats)
+      else:
+        asserts.assert_true(stat['num_failures'] == self.NUM_ITER,
+        "All one-sided RTT requests must fail when executed without privilege",
+                            extras=stats)
+        for code in stat['status_codes']:
+          asserts.assert_true(code ==
+            rconsts.EVENT_CB_RANGING_STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC,
+                              "Expected non-support error code", extras=stats)
+    asserts.explicit_pass("RTT test done", extras=stats)
diff --git a/acts/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py b/acts/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py
new file mode 100644
index 0000000..65b67d2
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeApNonSupporting11McTest(WifiBaseTest, RttBaseTest):
+  """Test class for RTT ranging to Access Points which do not support IEEE
+  802.11mc"""
+
+  # Number of RTT iterations
+  NUM_ITER = 10
+
+  # Time gap (in seconds) between iterations
+  TIME_BETWEEN_ITERATIONS = 0
+
+  def __init__(self, controllers):
+    WifiBaseTest.__init__(self, controllers)
+    RttBaseTest.__init__(self, controllers)
+    if "AccessPoint" in self.user_params:
+      self.legacy_configure_ap_and_start()
+
+  #############################################################################
+
+  @test_tracker_info(uuid="cde756e9-11f3-43da-b9ae-9edf85764f82")
+  def test_rtt_non_80211mc_supporting_aps(self):
+    """Scan for APs and perform RTT on non-IEEE 802.11mc supporting APs"""
+    dut = self.android_devices[0]
+    non_rtt_aps = rutils.select_best_scan_results(
+      rutils.scan_with_rtt_support_constraint(dut, False), select_count=1)
+    dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+    asserts.assert_true(len(non_rtt_aps) > 0, "Need at least one AP!")
+    events = rutils.run_ranging(dut, non_rtt_aps, self.NUM_ITER,
+                                self.TIME_BETWEEN_ITERATIONS)
+    stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+                                   self.rtt_reference_distance_margin_mm,
+                                   self.rtt_min_expected_rssi_dbm,
+                                   self.lci_reference, self.lcr_reference)
+    dut.log.debug("Stats=%s", stats)
+
+    for bssid, stat in stats.items():
+      asserts.assert_true(stat['num_no_results'] == 0,
+                          "Missing (timed-out) results", extras=stats)
+      asserts.assert_false(stat['any_lci_mismatch'],
+                           "LCI mismatch", extras=stats)
+      asserts.assert_false(stat['any_lcr_mismatch'],
+                           "LCR mismatch", extras=stats)
+      asserts.assert_equal(stat['num_invalid_rssi'], 0, "Invalid RSSI",
+                          extras=stats)
+      asserts.assert_true(stat['num_failures'] <=
+                          self.rtt_max_failure_rate_one_sided_rtt_percentage
+                          * stat['num_results'] / 100,
+                          "Failure rate is too high", extras=stats)
+      asserts.assert_true(stat['num_range_out_of_margin'] <=
+                self.rtt_max_margin_exceeded_rate_one_sided_rtt_percentage
+                          * stat['num_success_results'] / 100,
+                "Results exceeding error margin rate is too high",
+                extras=stats)
+    asserts.explicit_pass("RTT test done", extras=stats)
+
+  @test_tracker_info(uuid="c9e22185-16d4-4fe6-894f-5823587b3288")
+  def test_rtt_non_80211mc_supporting_aps_wo_privilege(self):
+    """Scan for APs and perform RTT on non-IEEE 802.11mc supporting APs with the
+    device not having privilege access (expect failures)."""
+    dut = self.android_devices[0]
+    rutils.config_privilege_override(dut, True)
+    non_rtt_aps = rutils.select_best_scan_results(
+      rutils.scan_with_rtt_support_constraint(dut, False), select_count=1)
+    dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+    asserts.assert_true(len(non_rtt_aps) > 0, "Need at least one AP!")
+    events = rutils.run_ranging(dut, non_rtt_aps, self.NUM_ITER,
+                                self.TIME_BETWEEN_ITERATIONS)
+    stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+                                   self.rtt_reference_distance_margin_mm,
+                                   self.rtt_min_expected_rssi_dbm,
+                                   self.lci_reference, self.lcr_reference)
+    dut.log.debug("Stats=%s", stats)
+
+    for bssid, stat in stats.items():
+      asserts.assert_true(stat['num_no_results'] == 0,
+                          "Missing (timed-out) results", extras=stats)
+      asserts.assert_true(stat['num_failures'] == self.NUM_ITER,
+        "All one-sided RTT requests must fail when executed without privilege",
+                          extras=stats)
+      for code in stat['status_codes']:
+        asserts.assert_true(code ==
+        rconsts.EVENT_CB_RANGING_STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC,
+                            "Expected non-support error code", extras=stats)
+    asserts.explicit_pass("RTT test done", extras=stats)
+
+  @test_tracker_info(uuid="e117af56-bd3f-40ae-a2fd-4175f0daa7fa")
+  def test_rtt_non_80211mc_supporting_ap_faked_as_supporting(self):
+    """Scan for APs which do not support IEEE 802.11mc, maliciously modify the
+    Responder config to indicate support and pass-through to service. Verify
+    that get an error result.
+    """
+    dut = self.android_devices[0]
+    non_rtt_aps = rutils.select_best_scan_results(
+      rutils.scan_with_rtt_support_constraint(dut, False), select_count=1)
+    dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+    asserts.assert_true(len(non_rtt_aps) > 0, "Need at least one AP!")
+    non_rtt_aps = non_rtt_aps[0:1] # pick first
+    non_rtt_aps[0][rconsts.SCAN_RESULT_KEY_RTT_RESPONDER] = True # falsify
+    dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+    events = rutils.run_ranging(dut, non_rtt_aps, self.NUM_ITER,
+                                self.TIME_BETWEEN_ITERATIONS)
+    stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+                                   self.rtt_reference_distance_margin_mm,
+                                   self.rtt_min_expected_rssi_dbm,
+                                   self.lci_reference, self.lcr_reference)
+    dut.log.debug("Stats=%s", stats)
+
+    for bssid, stat in stats.items():
+      asserts.assert_true(stat['num_no_results'] == 0,
+                          "Missing (timed-out) results", extras=stats)
+      asserts.assert_true(stat['num_failures'] == self.NUM_ITER,
+                          "Failures expected for falsified responder config",
+                          extras=stats)
+    asserts.explicit_pass("RTT test done", extras=stats)
diff --git a/acts/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py b/acts/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py
new file mode 100644
index 0000000..d889a22
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py
@@ -0,0 +1,187 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import queue
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeApSupporting11McTest(RttBaseTest):
+  """Test class for RTT ranging to Access Points which support IEEE 802.11mc"""
+
+  # Number of RTT iterations
+  NUM_ITER = 10
+
+  # Time gap (in seconds) between iterations
+  TIME_BETWEEN_ITERATIONS = 0
+
+  def __init__(self, controllers):
+    RttBaseTest.__init__(self, controllers)
+
+  #############################################################################
+
+  @test_tracker_info(uuid="6705270f-924b-4bef-b50a-0f0a7eb9ce52")
+  def test_rtt_80211mc_supporting_aps(self):
+    """Scan for APs and perform RTT only to those which support 802.11mc"""
+    dut = self.android_devices[0]
+    rtt_supporting_aps = rutils.select_best_scan_results(
+      rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+      select_count=2)
+    dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
+    events = rutils.run_ranging(dut, rtt_supporting_aps, self.NUM_ITER,
+                                self.TIME_BETWEEN_ITERATIONS)
+    stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+                                   self.rtt_reference_distance_margin_mm,
+                                   self.rtt_min_expected_rssi_dbm,
+                                   self.lci_reference, self.lcr_reference)
+    dut.log.debug("Stats=%s", stats)
+
+    for bssid, stat in stats.items():
+      asserts.assert_true(stat['num_no_results'] == 0,
+                          "Missing (timed-out) results", extras=stats)
+      asserts.assert_false(stat['any_lci_mismatch'],
+                           "LCI mismatch", extras=stats)
+      asserts.assert_false(stat['any_lcr_mismatch'],
+                           "LCR mismatch", extras=stats)
+      asserts.assert_false(stat['invalid_num_attempted'],
+                           "Invalid (0) number of attempts", extras=stats)
+      asserts.assert_false(stat['invalid_num_successful'],
+                           "Invalid (0) number of successes", extras=stats)
+      asserts.assert_equal(stat['num_invalid_rssi'], 0, "Invalid RSSI",
+                          extras=stats)
+      asserts.assert_true(stat['num_failures'] <=
+              self.rtt_max_failure_rate_two_sided_rtt_percentage
+                          * stat['num_results'] / 100,
+              "Failure rate is too high", extras=stats)
+      asserts.assert_true(stat['num_range_out_of_margin'] <=
+              self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+                          * stat['num_success_results'] / 100,
+              "Results exceeding error margin rate is too high", extras=stats)
+    asserts.explicit_pass("RTT test done", extras=stats)
+
+  #########################################################################
+  #
+  # LEGACY API test code
+  #
+  #########################################################################
+
+  @test_tracker_info(uuid="18be9737-2f03-4e35-9a23-f722dea7b82d")
+  def test_legacy_rtt_80211mc_supporting_aps(self):
+    """Scan for APs and perform RTT only to those which support 802.11mc - using
+    the LEGACY API!"""
+    dut = self.android_devices[0]
+    rtt_supporting_aps = rutils.select_best_scan_results(
+      rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+      select_count=2)
+    dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
+
+    rtt_configs = []
+    for ap in rtt_supporting_aps:
+      rtt_configs.append(self.rtt_config_from_scan_result(ap))
+    dut.log.debug("RTT configs=%s", rtt_configs)
+
+    results = []
+    num_missing = 0
+    num_failed_aborted = 0
+    for i in range(self.NUM_ITER):
+        idx = dut.droid.wifiRttStartRanging(rtt_configs)
+        event = None
+        try:
+          events = dut.ed.pop_events("WifiRttRanging%d" % idx, 30)
+          dut.log.debug("Event=%s", events)
+          for event in events:
+            if rconsts.EVENT_CB_RANGING_KEY_RESULTS in event["data"]:
+              results.append(
+                  event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS])
+            else:
+              self.log.info("RTT failed/aborted - %s", event)
+              results.append([])
+              num_failed_aborted = num_failed_aborted + 1
+        except queue.Empty:
+          self.log.debug("Waiting for RTT event timed out.")
+          results.append([])
+          num_missing = num_missing + 1
+
+    # basic error checking:
+    # 1. no missing
+    # 2. no full failed/aborted (i.e. operation not even tried)
+    # 3. overall (all BSSIDs) success rate > threshold
+    asserts.assert_equal(num_missing, 0,
+                         "Missing results (timeout waiting for event)",
+                         extras={"data":results})
+    asserts.assert_equal(num_failed_aborted, 0,
+                         "Failed or aborted operations (not tried)",
+                         extras={"data":results})
+
+    num_results = 0
+    num_errors = 0
+    for result_group in results:
+      num_results = num_results + len(result_group)
+      for result in result_group:
+        if result["status"] != 0:
+          num_errors = num_errors + 1
+
+    extras = [results, {"num_results": num_results, "num_errors": num_errors}]
+    asserts.assert_true(
+      num_errors <= self.rtt_max_failure_rate_two_sided_rtt_percentage
+        * num_results / 100,
+      "Failure rate is too high", extras={"data":extras})
+    asserts.explicit_pass("RTT test done", extras={"data": extras})
+
+  def rtt_config_from_scan_result(self, scan_result):
+    """Creates an Rtt configuration based on the scan result of a network.
+    """
+    WifiEnums = wutils.WifiEnums
+    ScanResult = WifiEnums.ScanResult
+    RttParam = WifiEnums.RttParam
+    RttBW = WifiEnums.RttBW
+    RttPreamble = WifiEnums.RttPreamble
+    RttType = WifiEnums.RttType
+
+    scan_result_channel_width_to_rtt = {
+      ScanResult.CHANNEL_WIDTH_20MHZ: RttBW.BW_20_SUPPORT,
+      ScanResult.CHANNEL_WIDTH_40MHZ: RttBW.BW_40_SUPPORT,
+      ScanResult.CHANNEL_WIDTH_80MHZ: RttBW.BW_80_SUPPORT,
+      ScanResult.CHANNEL_WIDTH_160MHZ: RttBW.BW_160_SUPPORT,
+      ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ: RttBW.BW_160_SUPPORT
+    }
+    p = {}
+    freq = scan_result[RttParam.frequency]
+    p[RttParam.frequency] = freq
+    p[RttParam.BSSID] = scan_result[WifiEnums.BSSID_KEY]
+    if freq > 5000:
+      p[RttParam.preamble] = RttPreamble.PREAMBLE_VHT
+    else:
+      p[RttParam.preamble] = RttPreamble.PREAMBLE_HT
+    cf0 = scan_result[RttParam.center_freq0]
+    if cf0 > 0:
+      p[RttParam.center_freq0] = cf0
+    cf1 = scan_result[RttParam.center_freq1]
+    if cf1 > 0:
+      p[RttParam.center_freq1] = cf1
+    cw = scan_result["channelWidth"]
+    p[RttParam.channel_width] = cw
+    p[RttParam.bandwidth] = scan_result_channel_width_to_rtt[cw]
+    if scan_result["is80211McRTTResponder"]:
+      p[RttParam.request_type] = RttType.TYPE_TWO_SIDED
+    else:
+      p[RttParam.request_type] = RttType.TYPE_ONE_SIDED
+    return p
diff --git a/acts/tests/google/wifi/rtt/functional/RangeAwareTest.py b/acts/tests/google/wifi/rtt/functional/RangeAwareTest.py
new file mode 100644
index 0000000..d4b7d41
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeAwareTest.py
@@ -0,0 +1,409 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import queue
+import time
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.aware import aware_const as aconsts
+from acts.test_utils.wifi.aware import aware_test_utils as autils
+from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeAwareTest(AwareBaseTest, RttBaseTest):
+  """Test class for RTT ranging to Wi-Fi Aware peers"""
+  SERVICE_NAME = "GoogleTestServiceXY"
+
+  # Number of RTT iterations
+  NUM_ITER = 10
+
+  # Time gap (in seconds) between iterations
+  TIME_BETWEEN_ITERATIONS = 0
+
+  # Time gap (in seconds) when switching between Initiator and Responder
+  TIME_BETWEEN_ROLES = 4
+
+  def __init__(self, controllers):
+    AwareBaseTest.__init__(self, controllers)
+    RttBaseTest.__init__(self, controllers)
+
+  def setup_test(self):
+    """Manual setup here due to multiple inheritance: explicitly execute the
+    setup method from both parents."""
+    AwareBaseTest.setup_test(self)
+    RttBaseTest.setup_test(self)
+
+  def teardown_test(self):
+    """Manual teardown here due to multiple inheritance: explicitly execute the
+    teardown method from both parents."""
+    AwareBaseTest.teardown_test(self)
+    RttBaseTest.teardown_test(self)
+
+  #############################################################################
+
+  def run_rtt_discovery(self, init_dut, resp_mac=None, resp_peer_id=None):
+    """Perform single RTT measurement, using Aware, from the Initiator DUT to
+    a Responder. The RTT Responder can be specified using its MAC address
+    (obtained using out- of-band discovery) or its Peer ID (using Aware
+    discovery).
+
+    Args:
+      init_dut: RTT Initiator device
+      resp_mac: MAC address of the RTT Responder device
+      resp_peer_id: Peer ID of the RTT Responder device
+    """
+    asserts.assert_true(resp_mac is not None or resp_peer_id is not None,
+                        "One of the Responder specifications (MAC or Peer ID)"
+                        " must be provided!")
+    if resp_mac is not None:
+      id = init_dut.droid.wifiRttStartRangingToAwarePeerMac(resp_mac)
+    else:
+      id = init_dut.droid.wifiRttStartRangingToAwarePeerId(resp_peer_id)
+    try:
+      event = init_dut.ed.pop_event(rutils.decorate_event(
+          rconsts.EVENT_CB_RANGING_ON_RESULT, id), rutils.EVENT_TIMEOUT)
+      result = event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0]
+      if resp_mac is not None:
+        rutils.validate_aware_mac_result(result, resp_mac, "DUT")
+      else:
+        rutils.validate_aware_peer_id_result(result, resp_peer_id, "DUT")
+      return result
+    except queue.Empty:
+      return None
+
+  def run_rtt_ib_discovery_set(self, do_both_directions, iter_count,
+      time_between_iterations, time_between_roles):
+    """Perform a set of RTT measurements, using in-band (Aware) discovery.
+
+    Args:
+      do_both_directions: False - perform all measurements in one direction,
+                          True - perform 2 measurements one in both directions.
+      iter_count: Number of measurements to perform.
+      time_between_iterations: Number of seconds to wait between iterations.
+      time_between_roles: Number of seconds to wait when switching between
+                          Initiator and Responder roles (only matters if
+                          do_both_directions=True).
+
+    Returns: a list of the events containing the RTT results (or None for a
+    failed measurement). If both directions are tested then returns a list of
+    2 elements: one set for each direction.
+    """
+    p_dut = self.android_devices[0]
+    s_dut = self.android_devices[1]
+
+    (p_id, s_id, p_disc_id, s_disc_id,
+     peer_id_on_sub, peer_id_on_pub) = autils.create_discovery_pair(
+        p_dut,
+        s_dut,
+        p_config=autils.add_ranging_to_pub(autils.create_discovery_config(
+            self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED), True),
+        s_config=autils.add_ranging_to_pub(autils.create_discovery_config(
+            self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE), True),
+        device_startup_offset=self.device_startup_offset,
+        msg_id=self.get_next_msg_id())
+
+    resultsPS = []
+    resultsSP = []
+    for i in range(iter_count):
+      if i != 0 and time_between_iterations != 0:
+        time.sleep(time_between_iterations)
+
+      # perform RTT from pub -> sub
+      resultsPS.append(
+        self.run_rtt_discovery(p_dut, resp_peer_id=peer_id_on_pub))
+
+      if do_both_directions:
+        if time_between_roles != 0:
+          time.sleep(time_between_roles)
+
+        # perform RTT from sub -> pub
+        resultsSP.append(
+          self.run_rtt_discovery(s_dut, resp_peer_id=peer_id_on_sub))
+
+    return resultsPS if not do_both_directions else [resultsPS, resultsSP]
+
+  def run_rtt_oob_discovery_set(self, do_both_directions, iter_count,
+      time_between_iterations, time_between_roles):
+    """Perform a set of RTT measurements, using out-of-band discovery.
+
+    Args:
+      do_both_directions: False - perform all measurements in one direction,
+                          True - perform 2 measurements one in both directions.
+      iter_count: Number of measurements to perform.
+      time_between_iterations: Number of seconds to wait between iterations.
+      time_between_roles: Number of seconds to wait when switching between
+                          Initiator and Responder roles (only matters if
+                          do_both_directions=True).
+      enable_ranging: True to enable Ranging, False to disable.
+
+    Returns: a list of the events containing the RTT results (or None for a
+    failed measurement). If both directions are tested then returns a list of
+    2 elements: one set for each direction.
+    """
+    dut0 = self.android_devices[0]
+    dut1 = self.android_devices[1]
+
+    id0, mac0 = autils.attach_with_identity(dut0)
+    id1, mac1 = autils.attach_with_identity(dut1)
+
+    # wait for for devices to synchronize with each other - there are no other
+    # mechanisms to make sure this happens for OOB discovery (except retrying
+    # to execute the data-path request)
+    time.sleep(autils.WAIT_FOR_CLUSTER)
+
+    # start publisher(s) on the Responder(s) with ranging enabled
+    p_config = autils.add_ranging_to_pub(
+      autils.create_discovery_config(self.SERVICE_NAME,
+                                     aconsts.PUBLISH_TYPE_UNSOLICITED),
+      enable_ranging=True)
+    dut1.droid.wifiAwarePublish(id1, p_config)
+    autils.wait_for_event(dut1, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+    if do_both_directions:
+      dut0.droid.wifiAwarePublish(id0, p_config)
+      autils.wait_for_event(dut0, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+    results01 = []
+    results10 = []
+    for i in range(iter_count):
+      if i != 0 and time_between_iterations != 0:
+        time.sleep(time_between_iterations)
+
+      # perform RTT from dut0 -> dut1
+      results01.append(
+          self.run_rtt_discovery(dut0, resp_mac=mac1))
+
+      if do_both_directions:
+        if time_between_roles != 0:
+          time.sleep(time_between_roles)
+
+        # perform RTT from dut1 -> dut0
+        results10.append(
+            self.run_rtt_discovery(dut1, resp_mac=mac0))
+
+    return results01 if not do_both_directions else [results01, results10]
+
+  def verify_results(self, results, results_reverse_direction=None):
+    """Verifies the results of the RTT experiment.
+
+    Args:
+      results: List of RTT results.
+      results_reverse_direction: List of RTT results executed in the
+                                reverse direction. Optional.
+    """
+    stats = rutils.extract_stats(results, self.rtt_reference_distance_mm,
+                                 self.rtt_reference_distance_margin_mm,
+                                 self.rtt_min_expected_rssi_dbm)
+    stats_reverse_direction = None
+    if results_reverse_direction is not None:
+      stats_reverse_direction = rutils.extract_stats(results_reverse_direction,
+          self.rtt_reference_distance_mm, self.rtt_reference_distance_margin_mm,
+          self.rtt_min_expected_rssi_dbm)
+    self.log.debug("Stats: %s", stats)
+    if stats_reverse_direction is not None:
+      self.log.debug("Stats in reverse direction: %s", stats_reverse_direction)
+
+    extras = stats if stats_reverse_direction is None else {
+      "forward": stats,
+      "reverse": stats_reverse_direction}
+
+    asserts.assert_true(stats['num_no_results'] == 0,
+                        "Missing (timed-out) results", extras=extras)
+    asserts.assert_false(stats['any_lci_mismatch'],
+                         "LCI mismatch", extras=extras)
+    asserts.assert_false(stats['any_lcr_mismatch'],
+                         "LCR mismatch", extras=extras)
+    asserts.assert_false(stats['invalid_num_attempted'],
+                         "Invalid (0) number of attempts", extras=stats)
+    asserts.assert_false(stats['invalid_num_successful'],
+                         "Invalid (0) number of successes", extras=stats)
+    asserts.assert_equal(stats['num_invalid_rssi'], 0, "Invalid RSSI",
+                         extras=extras)
+    asserts.assert_true(
+        stats['num_failures'] <=
+          self.rtt_max_failure_rate_two_sided_rtt_percentage
+          * stats['num_results'] / 100,
+        "Failure rate is too high", extras=extras)
+    asserts.assert_true(
+        stats['num_range_out_of_margin']
+          <= self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+             * stats['num_success_results'] / 100,
+        "Results exceeding error margin rate is too high", extras=extras)
+
+    if stats_reverse_direction is not None:
+      asserts.assert_true(stats_reverse_direction['num_no_results'] == 0,
+                          "Missing (timed-out) results",
+                          extras=extras)
+      asserts.assert_false(stats['any_lci_mismatch'],
+                           "LCI mismatch", extras=extras)
+      asserts.assert_false(stats['any_lcr_mismatch'],
+                           "LCR mismatch", extras=extras)
+      asserts.assert_equal(stats['num_invalid_rssi'], 0, "Invalid RSSI",
+                           extras=extras)
+      asserts.assert_true(
+          stats_reverse_direction['num_failures']
+            <= self.rtt_max_failure_rate_two_sided_rtt_percentage
+                * stats['num_results'] / 100,
+          "Failure rate is too high", extras=extras)
+      asserts.assert_true(
+          stats_reverse_direction['num_range_out_of_margin']
+            <= self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+                * stats['num_success_results'] / 100,
+          "Results exceeding error margin rate is too high",
+          extras=extras)
+
+    asserts.explicit_pass("RTT Aware test done", extras=extras)
+
+  #############################################################################
+
+  @test_tracker_info(uuid="9e4e7ab4-2254-498c-9788-21e15ed9a370")
+  def test_rtt_oob_discovery_one_way(self):
+    """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
+    to communicate the MAC addresses to the peer. Test one-direction RTT only.
+    """
+    rtt_results = self.run_rtt_oob_discovery_set(do_both_directions=False,
+          iter_count=self.NUM_ITER,
+          time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+          time_between_roles=self.TIME_BETWEEN_ROLES)
+    self.verify_results(rtt_results)
+
+  @test_tracker_info(uuid="22edba77-eeb2-43ee-875a-84437550ad84")
+  def test_rtt_oob_discovery_both_ways(self):
+    """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
+    to communicate the MAC addresses to the peer. Test RTT both-ways:
+    switching rapidly between Initiator and Responder.
+    """
+    rtt_results1, rtt_results2 = self.run_rtt_oob_discovery_set(
+        do_both_directions=True, iter_count=self.NUM_ITER,
+        time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+        time_between_roles=self.TIME_BETWEEN_ROLES)
+    self.verify_results(rtt_results1, rtt_results2)
+
+  @test_tracker_info(uuid="18cef4be-95b4-4f7d-a140-5165874e7d1c")
+  def test_rtt_ib_discovery_one_way(self):
+    """Perform RTT between 2 Wi-Fi Aware devices. Use in-band (Aware) discovery
+    to communicate the MAC addresses to the peer. Test one-direction RTT only.
+    """
+    rtt_results = self.run_rtt_ib_discovery_set(do_both_directions=False,
+           iter_count=self.NUM_ITER,
+           time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+           time_between_roles=self.TIME_BETWEEN_ROLES)
+    self.verify_results(rtt_results)
+
+  @test_tracker_info(uuid="c67c8e70-c417-42d9-9bca-af3a89f1ddd9")
+  def test_rtt_ib_discovery_both_ways(self):
+    """Perform RTT between 2 Wi-Fi Aware devices. Use in-band (Aware) discovery
+    to communicate the MAC addresses to the peer. Test RTT both-ways:
+    switching rapidly between Initiator and Responder.
+    """
+    rtt_results1, rtt_results2 = self.run_rtt_ib_discovery_set(
+        do_both_directions=True, iter_count=self.NUM_ITER,
+        time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+        time_between_roles=self.TIME_BETWEEN_ROLES)
+    self.verify_results(rtt_results1, rtt_results2)
+
+  @test_tracker_info(uuid="54f9693d-45e5-4979-adbb-1b875d217c0c")
+  def test_rtt_without_initiator_aware(self):
+    """Try to perform RTT operation when there is no local Aware session (on the
+    Initiator). The Responder is configured normally: Aware on and a Publisher
+    with Ranging enable. Should FAIL."""
+    init_dut = self.android_devices[0]
+    resp_dut = self.android_devices[1]
+
+    # Enable a Responder and start a Publisher
+    resp_id = resp_dut.droid.wifiAwareAttach(True)
+    autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+    resp_ident_event = autils.wait_for_event(resp_dut,
+                                         aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+    resp_mac = resp_ident_event['data']['mac']
+
+    resp_config = autils.add_ranging_to_pub(
+        autils.create_discovery_config(self.SERVICE_NAME,
+                                       aconsts.PUBLISH_TYPE_UNSOLICITED),
+        enable_ranging=True)
+    resp_dut.droid.wifiAwarePublish(resp_id, resp_config)
+    autils.wait_for_event(resp_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+    # Initiate an RTT to Responder (no Aware started on Initiator!)
+    results = []
+    num_no_responses = 0
+    num_successes = 0
+    for i in range(self.NUM_ITER):
+      result = self.run_rtt_discovery(init_dut, resp_mac=resp_mac)
+      self.log.debug("result: %s", result)
+      results.append(result)
+      if result is None:
+        num_no_responses = num_no_responses + 1
+      elif (result[rconsts.EVENT_CB_RANGING_KEY_STATUS]
+            == rconsts.EVENT_CB_RANGING_STATUS_SUCCESS):
+        num_successes = num_successes + 1
+
+    asserts.assert_equal(num_no_responses, 0, "No RTT response?",
+                         extras={"data":results})
+    asserts.assert_equal(num_successes, 0, "Aware RTT w/o Aware should FAIL!",
+                         extras={"data":results})
+    asserts.explicit_pass("RTT Aware test done", extras={"data":results})
+
+  @test_tracker_info(uuid="87a69053-8261-4928-8ec1-c93aac7f3a8d")
+  def test_rtt_without_responder_aware(self):
+    """Try to perform RTT operation when there is no peer Aware session (on the
+    Responder). Should FAIL."""
+    init_dut = self.android_devices[0]
+    resp_dut = self.android_devices[1]
+
+    # Enable a Responder and start a Publisher
+    resp_id = resp_dut.droid.wifiAwareAttach(True)
+    autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+    resp_ident_event = autils.wait_for_event(resp_dut,
+                                             aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+    resp_mac = resp_ident_event['data']['mac']
+
+    resp_config = autils.add_ranging_to_pub(
+        autils.create_discovery_config(self.SERVICE_NAME,
+                                       aconsts.PUBLISH_TYPE_UNSOLICITED),
+        enable_ranging=True)
+    resp_dut.droid.wifiAwarePublish(resp_id, resp_config)
+    autils.wait_for_event(resp_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+    # Disable Responder
+    resp_dut.droid.wifiAwareDestroy(resp_id)
+
+    # Enable the Initiator
+    init_id = init_dut.droid.wifiAwareAttach()
+    autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+    # Initiate an RTT to Responder (no Aware started on Initiator!)
+    results = []
+    num_no_responses = 0
+    num_successes = 0
+    for i in range(self.NUM_ITER):
+      result = self.run_rtt_discovery(init_dut, resp_mac=resp_mac)
+      self.log.debug("result: %s", result)
+      results.append(result)
+      if result is None:
+        num_no_responses = num_no_responses + 1
+      elif (result[rconsts.EVENT_CB_RANGING_KEY_STATUS]
+            == rconsts.EVENT_CB_RANGING_STATUS_SUCCESS):
+        num_successes = num_successes + 1
+
+    asserts.assert_equal(num_no_responses, 0, "No RTT response?",
+                         extras={"data":results})
+    asserts.assert_equal(num_successes, 0, "Aware RTT w/o Aware should FAIL!",
+                         extras={"data":results})
+    asserts.explicit_pass("RTT Aware test done", extras={"data":results})
diff --git a/acts/tests/google/wifi/rtt/functional/RangeSoftApTest.py b/acts/tests/google/wifi/rtt/functional/RangeSoftApTest.py
new file mode 100644
index 0000000..f0c4f4c
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RangeSoftApTest.py
@@ -0,0 +1,95 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2018 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeSoftApTest(RttBaseTest):
+  """Test class for RTT ranging to an Android Soft AP."""
+
+  # Soft AP SSID
+  SOFT_AP_SSID = "RTT_TEST_SSID"
+
+  # Soft AP Password (irrelevant)
+  SOFT_AP_PASSWORD = "ABCDEFGH"
+
+  # Number of RTT iterations
+  NUM_ITER = 10
+
+  def __init__(self, controllers):
+    RttBaseTest.__init__(self, controllers)
+
+  #########################################################################
+
+  @test_tracker_info(uuid="578f0725-31e3-4e60-ad62-0212d93cf5b8")
+  def test_rtt_to_soft_ap(self):
+    """Set up a Soft AP on one device and try performing an RTT ranging to it
+    from another device. The attempt must fail - RTT on Soft AP must be
+    disabled."""
+    sap = self.android_devices[0]
+    sap.pretty_name = "SoftAP"
+    client = self.android_devices[1]
+    client.pretty_name = "Client"
+
+    # start Soft AP
+    wutils.start_wifi_tethering(sap, self.SOFT_AP_SSID, self.SOFT_AP_PASSWORD,
+                                band=WIFI_CONFIG_APBAND_5G, hidden=False)
+
+    try:
+      # start scanning on the client
+      wutils.start_wifi_connection_scan_and_ensure_network_found(client,
+                                                             self.SOFT_AP_SSID)
+      scans = client.droid.wifiGetScanResults()
+      scanned_softap = None
+      for scanned_ap in scans:
+        if scanned_ap[wutils.WifiEnums.SSID_KEY] == self.SOFT_AP_SSID:
+          scanned_softap = scanned_ap
+          break
+
+      asserts.assert_false(scanned_softap == None, "Soft AP not found in scan!",
+                           extras=scans)
+
+      # validate that Soft AP does not advertise 802.11mc support
+      asserts.assert_false(
+        rconsts.SCAN_RESULT_KEY_RTT_RESPONDER in scanned_softap and
+        scanned_softap[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER],
+        "Soft AP advertises itself as supporting 802.11mc!",
+        extras=scanned_softap)
+
+      # falsify the SoftAP's support for IEEE 802.11 so we try a 2-sided RTT
+      scanned_softap[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER] = True # falsify
+
+      # actually try ranging to the Soft AP
+      events = rutils.run_ranging(client, [scanned_softap], self.NUM_ITER, 0)
+      stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+                                     self.rtt_reference_distance_margin_mm,
+                                     self.rtt_min_expected_rssi_dbm,
+                                     self.lci_reference, self.lcr_reference)
+
+      asserts.assert_equal(
+          stats[scanned_ap[wutils.WifiEnums.BSSID_KEY]]['num_failures'],
+          self.NUM_ITER, "Some RTT operations to Soft AP succeed!?",
+          extras=stats)
+
+      asserts.explicit_pass("SoftAP + RTT validation done", extras=events)
+    finally:
+      wutils.stop_wifi_tethering(sap)
diff --git a/acts/tests/google/wifi/rtt/functional/RttDisableTest.py b/acts/tests/google/wifi/rtt/functional/RttDisableTest.py
new file mode 100644
index 0000000..1816cd5
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RttDisableTest.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts import asserts
+from acts import utils
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+
+class RttDisableTest(WifiBaseTest, RttBaseTest):
+  """Test class for RTT ranging enable/disable flows."""
+
+  MODE_DISABLE_WIFI = 0
+  MODE_ENABLE_DOZE = 1
+  MODE_DISABLE_LOCATIONING = 2
+
+  def __init__(self, controllers):
+    WifiBaseTest.__init__(self, controllers)
+    RttBaseTest.__init__(self, controllers)
+    if "AccessPoint" in self.user_params:
+      self.legacy_configure_ap_and_start()
+
+  def run_disable_rtt(self, disable_mode):
+    """Validate the RTT disabled flows: whether by disabling Wi-Fi or entering
+    doze mode.
+
+    Args:
+      disable_mode: The particular mechanism in which RTT is disabled. One of
+                    the MODE_* constants.
+    """
+    dut = self.android_devices[0]
+
+    # validate start-up conditions
+    asserts.assert_true(dut.droid.wifiIsRttAvailable(), "RTT is not available")
+
+    # scan to get some APs to be used later
+    all_aps = rutils.select_best_scan_results(rutils.scan_networks(dut),
+                                              select_count=1)
+    asserts.assert_true(len(all_aps) > 0, "Need at least one visible AP!")
+
+    # disable RTT and validate broadcast & API
+    if disable_mode == self.MODE_DISABLE_WIFI:
+      # disabling Wi-Fi is not sufficient: since scan mode (and hence RTT) will
+      # remain enabled - we need to disable the Wi-Fi chip aka Airplane Mode
+      asserts.assert_true(utils.force_airplane_mode(dut, True),
+                          "Can not turn on airplane mode on: %s" % dut.serial)
+    elif disable_mode == self.MODE_ENABLE_DOZE:
+      asserts.assert_true(utils.enable_doze(dut), "Can't enable doze")
+    elif disable_mode == self.MODE_DISABLE_LOCATIONING:
+      utils.set_location_service(dut, False)
+
+    rutils.wait_for_event(dut, rconsts.BROADCAST_WIFI_RTT_NOT_AVAILABLE)
+    asserts.assert_false(dut.droid.wifiIsRttAvailable(), "RTT is available")
+
+    # request a range and validate error
+    id = dut.droid.wifiRttStartRangingToAccessPoints(all_aps[0:1])
+    event = rutils.wait_for_event(dut, rutils.decorate_event(
+        rconsts.EVENT_CB_RANGING_ON_FAIL, id))
+    asserts.assert_equal(event["data"][rconsts.EVENT_CB_RANGING_KEY_STATUS],
+                         rconsts.RANGING_FAIL_CODE_RTT_NOT_AVAILABLE,
+                         "Invalid error code")
+
+    # enable RTT and validate broadcast & API
+    if disable_mode == self.MODE_DISABLE_WIFI:
+      asserts.assert_true(utils.force_airplane_mode(dut, False),
+                          "Can not turn off airplane mode on: %s" % dut.serial)
+    elif disable_mode == self.MODE_ENABLE_DOZE:
+      asserts.assert_true(utils.disable_doze(dut), "Can't disable doze")
+    elif disable_mode == self.MODE_DISABLE_LOCATIONING:
+      utils.set_location_service(dut, True)
+
+    rutils.wait_for_event(dut, rconsts.BROADCAST_WIFI_RTT_AVAILABLE)
+    asserts.assert_true(dut.droid.wifiIsRttAvailable(), "RTT is not available")
+
+  ############################################################################
+
+  @test_tracker_info(uuid="498c49ab-a188-4612-998d-c47b35ff285e")
+  def test_disable_wifi(self):
+    """Validate that getting expected broadcast when Wi-Fi is disabled and that
+    any range requests are rejected."""
+    self.run_disable_rtt(self.MODE_DISABLE_WIFI)
+
+  @test_tracker_info(uuid="f71f731f-4aaf-402b-8595-db94b625b544")
+  def test_enable_doze(self):
+    """Validate that getting expected broadcast when RTT is disabled due to doze
+    mode and that any range requests are rejected."""
+    self.run_disable_rtt(self.MODE_ENABLE_DOZE)
+
+  @test_tracker_info(uuid="6a1c83a8-9eaf-49db-b547-5131cba0eafe")
+  def test_disable_location(self):
+    """Validate that getting expected broadcast when locationing is disabled and
+    that any range requests are rejected."""
+    self.run_disable_rtt(self.MODE_DISABLE_LOCATIONING)
diff --git a/acts/tests/google/wifi/rtt/functional/RttRequestManagementTest.py b/acts/tests/google/wifi/rtt/functional/RttRequestManagementTest.py
new file mode 100644
index 0000000..82c1058
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/functional/RttRequestManagementTest.py
@@ -0,0 +1,140 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+import random
+import time
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RttRequestManagementTest(RttBaseTest):
+  """Test class for RTT request management flows."""
+
+  SPAMMING_LIMIT = 20
+
+  def __init__(self, controllers):
+    RttBaseTest.__init__(self, controllers)
+
+  #############################################################################
+
+  @test_tracker_info(uuid="29ff4a02-2952-47df-bf56-64f30c963093")
+  def test_cancel_ranging(self):
+    """Request a 'large' number of range operations with various UIDs (using the
+    work-source API), then cancel some of them.
+
+    We can't guarantee a reaction time - it is possible that a cancelled test
+    was already finished and it's results dispatched back. The test therefore
+    stacks the request queue. The sequence is:
+
+    - Request:
+      - 50 tests @ UIDs = {uid1, uid2, uid3}
+      - 2 tests @ UIDs = {uid2, uid3}
+      - 1 test2 @ UIDs = {uid1, uid2, uid3}
+    - Cancel UIDs = {uid2, uid3}
+
+    Expect to receive only 51 results.
+    """
+    dut = self.android_devices[0]
+    max_peers = dut.droid.wifiRttMaxPeersInRequest()
+
+    all_uids = [1000, 20, 30] # 1000 = System Server (makes requests foreground)
+    some_uids = [20, 30]
+
+    aps = rutils.select_best_scan_results(
+      rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+      select_count=1)
+    dut.log.info("RTT Supporting APs=%s", aps)
+
+    asserts.assert_true(
+        len(aps) > 0,
+        "Need at least one AP which supports 802.11mc!")
+    if len(aps) > max_peers:
+      aps = aps[0:max_peers]
+
+    group1_ids = []
+    group2_ids = []
+    group3_ids = []
+
+    # step 1: request <spam_limit> ranging operations on [uid1, uid2, uid3]
+    for i in range(self.SPAMMING_LIMIT):
+      group1_ids.append(
+        dut.droid.wifiRttStartRangingToAccessPoints(aps, all_uids))
+
+    # step 2: request 2 ranging operations on [uid2, uid3]
+    for i in range(2):
+      group2_ids.append(
+        dut.droid.wifiRttStartRangingToAccessPoints(aps, some_uids))
+
+    # step 3: request 1 ranging operation on [uid1, uid2, uid3]
+    for i in range(1):
+      group3_ids.append(
+          dut.droid.wifiRttStartRangingToAccessPoints(aps, all_uids))
+
+    # step 4: cancel ranging requests on [uid2, uid3]
+    dut.droid.wifiRttCancelRanging(some_uids)
+
+    # collect results
+    for i in range(len(group1_ids)):
+      rutils.wait_for_event(dut, rutils.decorate_event(
+        rconsts.EVENT_CB_RANGING_ON_RESULT, group1_ids[i]))
+    time.sleep(rutils.EVENT_TIMEOUT) # optimize time-outs below to single one
+    for i in range(len(group2_ids)):
+      rutils.fail_on_event(dut, rutils.decorate_event(
+          rconsts.EVENT_CB_RANGING_ON_RESULT, group2_ids[i]), 0)
+    for i in range(len(group3_ids)):
+      rutils.wait_for_event(dut, rutils.decorate_event(
+          rconsts.EVENT_CB_RANGING_ON_RESULT, group3_ids[i]))
+
+  @test_tracker_info(uuid="48297480-c026-4780-8c13-476e7bea440c")
+  def test_throttling(self):
+    """Request sequential range operations using a bogus UID (which will
+    translate as a throttled process) and similarly using the ACTS/sl4a as
+    the source (a foreground/unthrottled process)."""
+    dut = self.android_devices[0]
+    max_peers = dut.droid.wifiRttMaxPeersInRequest()
+
+    # Need to use a random number since the system keeps states and so the
+    # background uid will be throttled on the next run of this script
+    fake_uid = [random.randint(10, 9999)]
+
+    aps = rutils.select_best_scan_results(
+      rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+      select_count=1)
+    dut.log.info("RTT Supporting APs=%s", aps)
+
+    asserts.assert_true(
+        len(aps) > 0,
+        "Need at least one AP which supports 802.11mc!")
+    if len(aps) > max_peers:
+      aps = aps[0:max_peers]
+
+    id1 = dut.droid.wifiRttStartRangingToAccessPoints(aps) # as ACTS/sl4a
+    id2 = dut.droid.wifiRttStartRangingToAccessPoints(aps, fake_uid)
+    id3 = dut.droid.wifiRttStartRangingToAccessPoints(aps, fake_uid)
+    id4 = dut.droid.wifiRttStartRangingToAccessPoints(aps) # as ACTS/sl4a
+
+    rutils.wait_for_event(dut, rutils.decorate_event(
+      rconsts.EVENT_CB_RANGING_ON_RESULT, id1))
+    rutils.wait_for_event(dut, rutils.decorate_event(
+        rconsts.EVENT_CB_RANGING_ON_RESULT, id2))
+    rutils.wait_for_event(dut, rutils.decorate_event(
+        rconsts.EVENT_CB_RANGING_ON_FAIL, id3))
+    rutils.wait_for_event(dut, rutils.decorate_event(
+        rconsts.EVENT_CB_RANGING_ON_RESULT, id4))
diff --git a/acts/tests/google/wifi/rtt/stress/StressRangeApTest.py b/acts/tests/google/wifi/rtt/stress/StressRangeApTest.py
new file mode 100644
index 0000000..497c125
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/stress/StressRangeApTest.py
@@ -0,0 +1,79 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2017 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from acts import asserts
+from acts.base_test import BaseTestClass
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class StressRangeApTest(RttBaseTest):
+  """Test class for stress testing of RTT ranging to Access Points"""
+
+  def __init__(self, controllers):
+    BaseTestClass.__init__(self, controllers)
+
+  #############################################################################
+
+  def test_rtt_supporting_ap_only(self):
+    """Scan for APs and perform RTT only to those which support 802.11mc.
+
+    Stress test: repeat ranging to the same AP. Verify rate of success and
+    stability of results.
+    """
+    dut = self.android_devices[0]
+    rtt_supporting_aps = rutils.scan_with_rtt_support_constraint(dut, True,
+                                                                 repeat=10)
+    dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
+
+    num_iter = self.stress_test_min_iteration_count
+
+    max_peers = dut.droid.wifiRttMaxPeersInRequest()
+    asserts.assert_true(
+        len(rtt_supporting_aps) > 0,
+        "Need at least one AP which supports 802.11mc!")
+    if len(rtt_supporting_aps) > max_peers:
+      rtt_supporting_aps = rtt_supporting_aps[0:max_peers]
+
+    events = rutils.run_ranging(dut, rtt_supporting_aps, num_iter, 0,
+                                self.stress_test_target_run_time_sec)
+    stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+                                   self.rtt_reference_distance_margin_mm,
+                                   self.rtt_min_expected_rssi_dbm,
+                                   self.lci_reference, self.lcr_reference,
+                                   summary_only=True)
+    dut.log.debug("Stats=%s", stats)
+
+    for bssid, stat in stats.items():
+      asserts.assert_true(stat['num_no_results'] == 0,
+                          "Missing (timed-out) results", extras=stats)
+      asserts.assert_false(stat['any_lci_mismatch'],
+                           "LCI mismatch", extras=stats)
+      asserts.assert_false(stat['any_lcr_mismatch'],
+                           "LCR mismatch", extras=stats)
+      asserts.assert_equal(stat['num_invalid_rssi'], 0, "Invalid RSSI",
+                          extras=stats)
+      asserts.assert_true(stat['num_failures'] <=
+                          self.rtt_max_failure_rate_two_sided_rtt_percentage
+                          * stat['num_results'] / 100,
+                          "Failure rate is too high", extras=stats)
+      asserts.assert_true(stat['num_range_out_of_margin'] <=
+                    self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+                    * stat['num_success_results'] / 100,
+                    "Results exceeding error margin rate is too high",
+                    extras=stats)
+    asserts.explicit_pass("RTT test done", extras=stats)
+
diff --git a/acts/tests/google/wifi/rtt/stress/StressRangeAwareTest.py b/acts/tests/google/wifi/rtt/stress/StressRangeAwareTest.py
new file mode 100644
index 0000000..3073898
--- /dev/null
+++ b/acts/tests/google/wifi/rtt/stress/StressRangeAwareTest.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python3.4
+#
+#   Copyright 2018 - 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 queue
+import time
+
+from acts import asserts
+from acts.test_utils.wifi.aware import aware_const as aconsts
+from acts.test_utils.wifi.aware import aware_test_utils as autils
+from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class StressRangeAwareTest(AwareBaseTest, RttBaseTest):
+  """Test class for stress testing of RTT ranging to Wi-Fi Aware peers."""
+  SERVICE_NAME = "GoogleTestServiceXY"
+
+  def __init__(self, controllers):
+    AwareBaseTest.__init__(self, controllers)
+    RttBaseTest.__init__(self, controllers)
+
+  def setup_test(self):
+    """Manual setup here due to multiple inheritance: explicitly execute the
+    setup method from both parents."""
+    AwareBaseTest.setup_test(self)
+    RttBaseTest.setup_test(self)
+
+  def teardown_test(self):
+    """Manual teardown here due to multiple inheritance: explicitly execute the
+    teardown method from both parents."""
+    AwareBaseTest.teardown_test(self)
+    RttBaseTest.teardown_test(self)
+
+  #############################################################################
+
+  def run_rtt_discovery(self, init_dut, resp_mac=None, resp_peer_id=None):
+    """Perform single RTT measurement, using Aware, from the Initiator DUT to
+    a Responder. The RTT Responder can be specified using its MAC address
+    (obtained using out- of-band discovery) or its Peer ID (using Aware
+    discovery).
+
+    Args:
+      init_dut: RTT Initiator device
+      resp_mac: MAC address of the RTT Responder device
+      resp_peer_id: Peer ID of the RTT Responder device
+    """
+    asserts.assert_true(resp_mac is not None or resp_peer_id is not None,
+                        "One of the Responder specifications (MAC or Peer ID)"
+                        " must be provided!")
+    if resp_mac is not None:
+      id = init_dut.droid.wifiRttStartRangingToAwarePeerMac(resp_mac)
+    else:
+      id = init_dut.droid.wifiRttStartRangingToAwarePeerId(resp_peer_id)
+    try:
+      event = init_dut.ed.pop_event(rutils.decorate_event(
+          rconsts.EVENT_CB_RANGING_ON_RESULT, id), rutils.EVENT_TIMEOUT)
+      result = event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0]
+      if resp_mac is not None:
+        rutils.validate_aware_mac_result(result, resp_mac, "DUT")
+      else:
+        rutils.validate_aware_peer_id_result(result, resp_peer_id, "DUT")
+      return result
+    except queue.Empty:
+      return None
+
+  def test_stress_rtt_ib_discovery_set(self):
+    """Perform a set of RTT measurements, using in-band (Aware) discovery, and
+    switching Initiator and Responder roles repeatedly.
+
+    Stress test: repeat ranging operations. Verify rate of success and
+    stability of results.
+    """
+    p_dut = self.android_devices[0]
+    s_dut = self.android_devices[1]
+
+    (p_id, s_id, p_disc_id, s_disc_id,
+     peer_id_on_sub, peer_id_on_pub) = autils.create_discovery_pair(
+        p_dut,
+        s_dut,
+        p_config=autils.add_ranging_to_pub(autils.create_discovery_config(
+            self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED), True),
+        s_config=autils.add_ranging_to_pub(autils.create_discovery_config(
+            self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE), True),
+        device_startup_offset=self.device_startup_offset,
+        msg_id=self.get_next_msg_id())
+
+    results = []
+    start_clock = time.time()
+    iterations_done = 0
+    run_time = 0
+    while iterations_done < self.stress_test_min_iteration_count or (
+            self.stress_test_target_run_time_sec != 0
+        and run_time < self.stress_test_target_run_time_sec):
+      results.append(self.run_rtt_discovery(p_dut, resp_peer_id=peer_id_on_pub))
+      results.append(self.run_rtt_discovery(s_dut, resp_peer_id=peer_id_on_sub))
+
+      iterations_done = iterations_done + 1
+      run_time = time.time() - start_clock
+
+    stats = rutils.extract_stats(results, self.rtt_reference_distance_mm,
+                                 self.rtt_reference_distance_margin_mm,
+                                 self.rtt_min_expected_rssi_dbm,
+                                 summary_only=True)
+    self.log.debug("Stats: %s", stats)
+    asserts.assert_true(stats['num_no_results'] == 0,
+                        "Missing (timed-out) results", extras=stats)
+    asserts.assert_false(stats['any_lci_mismatch'],
+                         "LCI mismatch", extras=stats)
+    asserts.assert_false(stats['any_lcr_mismatch'],
+                         "LCR mismatch", extras=stats)
+    asserts.assert_equal(stats['num_invalid_rssi'], 0, "Invalid RSSI",
+                         extras=stats)
+    asserts.assert_true(
+        stats['num_failures'] <=
+        self.rtt_max_failure_rate_two_sided_rtt_percentage
+        * stats['num_results'] / 100,
+        "Failure rate is too high", extras=stats)
+    asserts.assert_true(
+        stats['num_range_out_of_margin']
+        <= self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+        * stats['num_success_results'] / 100,
+        "Results exceeding error margin rate is too high", extras=stats)