Merge "Bluetooth HID: fix flakey test" into pi-dev
diff --git a/acts/framework/acts/asserts.py b/acts/framework/acts/asserts.py
index 939ff9c..5046b3b 100644
--- a/acts/framework/acts/asserts.py
+++ b/acts/framework/acts/asserts.py
@@ -44,6 +44,7 @@
         extras: An optional field for extra information to be included in
                 test result.
     """
+    my_msg = None
     try:
         _pyunit_proxy.assertEqual(first, second)
     except Exception as e:
@@ -55,6 +56,8 @@
         my_msg = str(e)
         if msg:
             my_msg = "%s %s" % (my_msg, msg)
+    # This is a hack to remove the stacktrace produced by the above exception.
+    if my_msg is not None:
         fail(my_msg, extras=extras)
 
 
@@ -75,6 +78,7 @@
     :param extras: Extra object passed to test failure handler
     :return:
     """
+    my_msg = None
     try:
         if delta:
             _pyunit_proxy.assertAlmostEqual(
@@ -86,6 +90,8 @@
         my_msg = str(e)
         if msg:
             my_msg = "%s %s" % (my_msg, msg)
+    # This is a hack to remove the stacktrace produced by the above exception.
+    if my_msg is not None:
         fail(my_msg, extras=extras)
 
 
diff --git a/acts/framework/acts/base_test.py b/acts/framework/acts/base_test.py
index f66058a..d5b0db9 100755
--- a/acts/framework/acts/base_test.py
+++ b/acts/framework/acts/base_test.py
@@ -16,14 +16,15 @@
 import logging
 import os
 import traceback
+from concurrent.futures import ThreadPoolExecutor
 
 from acts import asserts
+from acts import keys
 from acts import logger
 from acts import records
 from acts import signals
 from acts import tracelogger
 from acts import utils
-from concurrent.futures import ThreadPoolExecutor
 
 # Macro strings for test result reporting
 TEST_CASE_TOKEN = "[Test Case]"
@@ -185,7 +186,6 @@
             self.log.warning(
                 'Unable to send BEGIN log command to all devices.')
             self.log.warning('Error: %s' % e)
-            pass
         return self.setup_test()
 
     def setup_test(self):
@@ -204,12 +204,14 @@
         """Proxy function to guarantee the base implementation of teardown_test
         is called.
         """
+        self.log.debug('Tearing down test %s' % test_name)
         try:
             # Write test end token to adb log if android device is attached.
             for ad in self.android_devices:
                 ad.droid.logV("%s END %s" % (TEST_CASE_TOKEN, test_name))
-        except:
-            pass
+        except Exception as e:
+            self.log.warning('Unable to send END log command to all devices.')
+            self.log.warning('Error: %s' % e)
         try:
             self.teardown_test()
         finally:
@@ -400,7 +402,11 @@
                     tr_record.add_error("teardown_test", e)
                     self._exec_procedure_func(self._on_exception, tr_record)
         except (signals.TestFailure, AssertionError) as e:
-            self.log.error(e)
+            if self.user_params.get(
+                    keys.Config.key_test_failure_tracebacks.value, False):
+                self.log.exception(e)
+            else:
+                self.log.error(e)
             tr_record.test_fail(e)
             self._exec_procedure_func(self._on_fail, tr_record)
         except signals.TestSkip as e:
@@ -500,10 +506,10 @@
 
             if format_args:
                 self.exec_one_testcase(test_name, test_func,
-                                       args + (setting, ), **kwargs)
+                                       args + (setting,), **kwargs)
             else:
                 self.exec_one_testcase(test_name, test_func,
-                                       (setting, ) + args, **kwargs)
+                                       (setting,) + args, **kwargs)
 
             if len(self.results.passed) - previous_success_cnt != 1:
                 failed_settings.append(setting)
diff --git a/acts/framework/acts/config_parser.py b/acts/framework/acts/config_parser.py
index ac6380e..498ce38 100755
--- a/acts/framework/acts/config_parser.py
+++ b/acts/framework/acts/config_parser.py
@@ -25,7 +25,8 @@
 
 # An environment variable defining the base location for ACTS logs.
 _ENV_ACTS_LOGPATH = 'ACTS_LOGPATH'
-
+# An environment variable that enables test case failures to log stack traces.
+_ENV_TEST_FAILURE_TRACEBACKS = 'ACTS_TEST_FAILURE_TRACEBACKS'
 # An environment variable defining the test search paths for ACTS.
 _ENV_ACTS_TESTPATHS = 'ACTS_TESTPATHS'
 _PATH_SEPARATOR = ':'
@@ -284,6 +285,10 @@
               (os.environ[_ENV_ACTS_TESTPATHS]))
         configs[keys.Config.key_test_paths.value] = os.environ[
             _ENV_ACTS_TESTPATHS].split(_PATH_SEPARATOR)
+    if (keys.Config.key_test_failure_tracebacks not in configs
+            and _ENV_TEST_FAILURE_TRACEBACKS in os.environ):
+        configs[keys.Config.key_test_failure_tracebacks.value] = os.environ[
+            _ENV_TEST_FAILURE_TRACEBACKS]
 
     # Add the global paths to the global config.
     k_log_path = keys.Config.key_log_path.value
diff --git a/acts/framework/acts/controllers/access_point.py b/acts/framework/acts/controllers/access_point.py
index d293ec6..f537bc8 100755
--- a/acts/framework/acts/controllers/access_point.py
+++ b/acts/framework/acts/controllers/access_point.py
@@ -145,8 +145,11 @@
         Bring down hostapd if instance is running, bring down all bridge
         interfaces.
         """
-        # Stop hostapd instance if running
         try:
+            # This is necessary for Gale/Whirlwind flashed with dev channel image
+            # Unused interfaces such as existing hostapd daemon, guest, mesh
+            # interfaces need to be brought down as part of the AP initialization
+            # process, otherwise test would fail.
             try:
                 self.ssh.run('stop hostapd')
             except job.Error:
diff --git a/acts/framework/acts/keys.py b/acts/framework/acts/keys.py
index 083d44c..526ef71 100644
--- a/acts/framework/acts/keys.py
+++ b/acts/framework/acts/keys.py
@@ -35,6 +35,7 @@
     key_address = "Address"
     key_random = "random"
     key_test_case_iterations = "test_case_iterations"
+    key_test_failure_tracebacks = "test_failure_tracebacks"
     # Config names for controllers packaged in ACTS.
     key_android_device = "AndroidDevice"
     key_chameleon_device = "ChameleonDevice"
diff --git a/acts/framework/acts/test_utils/bt/bt_coc_test_utils.py b/acts/framework/acts/test_utils/bt/bt_coc_test_utils.py
index 3dbf60c..f7d4867 100644
--- a/acts/framework/acts/test_utils/bt/bt_coc_test_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_coc_test_utils.py
@@ -20,11 +20,13 @@
 
 from acts.test_utils.bt.bt_constants import bt_default_timeout
 from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
+from acts.test_utils.bt.bt_constants import default_le_connection_interval_ms
 from acts.test_utils.bt.bt_constants import default_le_data_length
 from acts.test_utils.bt.bt_constants import gatt_phy
 from acts.test_utils.bt.bt_constants import gatt_transport
 from acts.test_utils.bt.bt_constants import l2cap_coc_header_size
-from acts.test_utils.bt.bt_constants import le_connection_interval_time_step
+from acts.test_utils.bt.bt_constants import le_connection_event_time_step_ms
+from acts.test_utils.bt.bt_constants import le_connection_interval_time_step_ms
 from acts.test_utils.bt.bt_constants import le_default_supervision_timeout
 from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
 from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
@@ -130,7 +132,9 @@
         secured_conn=False,
         le_connection_interval=0,
         le_tx_data_length=default_le_data_length,
-        accept_timeout_ms=default_bluetooth_socket_timeout_ms):
+        accept_timeout_ms=default_bluetooth_socket_timeout_ms,
+        le_min_ce_len=0,
+        le_max_ce_len=0):
     """Sets up the CoC connection between two Android devices.
 
     Args:
@@ -171,9 +175,11 @@
     # Adjust the Connection Interval (if necessary)
     bluetooth_gatt_1 = -1
     gatt_callback_1 = -1
-    if (le_connection_interval != 0) and is_ble:
+    gatt_connected = False
+    if is_ble and (le_connection_interval != 0 or le_min_ce_len != 0 or le_max_ce_len != 0):
         client_ad.log.info(
-            "Adjusting connection interval={}".format(le_connection_interval))
+            "Adjusting connection interval={}, le_min_ce_len={}, le_max_ce_len={}"
+            .format(le_connection_interval, le_min_ce_len, le_max_ce_len))
         try:
             bluetooth_gatt_1, gatt_callback_1 = setup_gatt_connection(
                 client_ad,
@@ -183,20 +189,34 @@
                 opportunistic=False)
         except GattTestUtilsError as err:
             client_ad.log.error(err)
+            if (adv_callback != None):
+                server_ad.droid.bleStopBleAdvertising(adv_callback)
             return False, None, None
         client_ad.log.info("setup_gatt_connection returns success")
-        minInterval = le_connection_interval / le_connection_interval_time_step
-        maxInterval = le_connection_interval / le_connection_interval_time_step
+        if (le_connection_interval != 0):
+            minInterval = le_connection_interval / le_connection_interval_time_step_ms
+            maxInterval = le_connection_interval / le_connection_interval_time_step_ms
+        else:
+            minInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms
+            maxInterval = default_le_connection_interval_ms / le_connection_interval_time_step_ms
+        if (le_min_ce_len != 0):
+            le_min_ce_len = le_min_ce_len / le_connection_event_time_step_ms
+        if (le_max_ce_len != 0):
+            le_max_ce_len = le_max_ce_len / le_connection_event_time_step_ms
+
         return_status = client_ad.droid.gattClientRequestLeConnectionParameters(
             bluetooth_gatt_1, minInterval, maxInterval, 0,
-            le_default_supervision_timeout, 0, 0)
+            le_default_supervision_timeout, le_min_ce_len, le_max_ce_len)
         if not return_status:
             client_ad.log.error(
                 "gattClientRequestLeConnectionParameters returns failure")
+            if (adv_callback != None):
+                server_ad.droid.bleStopBleAdvertising(adv_callback)
             return False, None, None
         client_ad.log.info(
             "gattClientRequestLeConnectionParameters returns success. Interval={}"
             .format(minInterval))
+        gatt_connected = True
         # For now, we will only test with 1 Mbit Phy.
         # TODO: Add explicit tests with 2 MBit Phy.
         client_ad.droid.gattClientSetPreferredPhy(
@@ -226,9 +246,13 @@
                 test_result = True
                 break
         time.sleep(1)
+
+    if (adv_callback != None):
+        server_ad.droid.bleStopBleAdvertising(adv_callback)
+
     if not test_result:
         client_ad.log.error("Failed to establish an CoC connection")
-        return False, None
+        return False, None, None
 
     if len(client_ad.droid.bluetoothSocketConnActiveConnections()) > 0:
         server_ad.log.info(
@@ -238,14 +262,27 @@
         server_ad.log.info("Error CoC client_ad Connection Inactive")
         client_ad.log.info("Error CoC client_ad Connection Inactive")
 
-    # Get the conn_id
-    client_conn_id = client_ad.droid.bluetoothGetLastConnId()
-    server_conn_id = server_ad.droid.bluetoothGetLastConnId()
+    # Wait for the client to be ready
+    client_conn_id = None
+    while (client_conn_id == None):
+        client_conn_id = client_ad.droid.bluetoothGetLastConnId()
+        if (client_conn_id != None):
+            break
+        time.sleep(1)
+
+    # Wait for the server to be ready
+    server_conn_id = None
+    while (server_conn_id == None):
+        server_conn_id = server_ad.droid.bluetoothGetLastConnId()
+        if (server_conn_id != None):
+            break
+        time.sleep(1)
+
     client_ad.log.info(
         "orchestrate_coc_connection: client conn id={}, server conn id={}".
         format(client_conn_id, server_conn_id))
 
-    if (le_connection_interval != 0) and is_ble:
+    if gatt_connected:
         disconnect_gatt_connection(client_ad, bluetooth_gatt_1,
                                    gatt_callback_1)
         client_ad.droid.gattClientClose(bluetooth_gatt_1)
diff --git a/acts/framework/acts/test_utils/bt/bt_constants.py b/acts/framework/acts/test_utils/bt/bt_constants.py
index bda16a6..20f68dd 100644
--- a/acts/framework/acts/test_utils/bt/bt_constants.py
+++ b/acts/framework/acts/test_utils/bt/bt_constants.py
@@ -32,9 +32,11 @@
 l2cap_max_inactivity_delay_after_disconnect = 5
 
 # LE specifications related constants
-le_connection_interval_time_step = 1.25
+le_connection_interval_time_step_ms = 1.25
 le_default_supervision_timeout = 2000
 default_le_data_length = 23
+default_le_connection_interval_ms = 30
+le_connection_event_time_step_ms = 0.625
 
 # Headers of LE L2CAP Connection-oriented Channels. See section 3.4, Vol 3, Part A, Version 5.0.
 l2cap_header_size = 4
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 ccebdbf..91d37cc 100644
--- a/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+# /usr/bin/env python3.4
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
@@ -24,7 +24,7 @@
 import re
 import random
 import string
-
+from time import sleep
 from acts.utils import exe_cmd
 import queue
 
@@ -283,6 +283,10 @@
     phone_phonebook_path = "{}{}".format(STORAGE_PATH, vcf_file)
     device.adb.push("{} {}".format(local_phonebook_path, phone_phonebook_path))
     device.droid.importVcf("file://{}{}".format(STORAGE_PATH, vcf_file))
+    # keyevent to allow contacts import from vcf file
+    sleep(1)
+    for key in ["ENTER", "DPAD_RIGHT", "ENTER"]:
+        device.adb.shell("input keyevent KEYCODE_{}".format(key))
     if wait_for_phone_number_update_complete(device, number_count):
         return number_count
     else:
@@ -299,6 +303,15 @@
     return True
 
 
+def delete_vcf_files(device):
+    """Deletes all files with .vcf extension
+    """
+    files = device.adb.shell("ls {}".format(STORAGE_PATH))
+    for file_name in files.split():
+        if ".vcf" in file_name:
+            device.adb.shell("rm -f {}{}".format(STORAGE_PATH, file_name))
+
+
 def erase_contacts(device):
     """Erase all contacts out of devices contact database.
     """
@@ -379,8 +392,8 @@
         for i in range(len(pse_call_log)):
             # Compare the phone number
             if normalize_phonenumber(pse_call_log[i][
-                    "number"]) != normalize_phonenumber(pce_call_log[i][
-                        "number"]):
+                                         "number"]) != normalize_phonenumber(pce_call_log[i][
+                                                                                 "number"]):
                 log.warning("Call Log numbers differ")
                 call_logs_match = False
 
@@ -391,7 +404,7 @@
 
             # Compare time to truncated second.
             if int(pse_call_log[i]["date"]) // 1000 != int(pce_call_log[i][
-                    "date"]) // 1000:
+                                                               "date"]) // 1000:
                 log.warning("Call log times don't match, check timezone.")
                 call_logs_match = False
 
diff --git a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
index 95c7b95..edf1bfa 100755
--- a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
+++ b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py
@@ -13,21 +13,14 @@
 #   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.
-"""
-    Base Class for Defining Common WiFi Test Functionality
-"""
+"""Base Class for Defining Common WiFi Test Functionality"""
 
 import copy
 import itertools
-import time
-
-import acts.controllers.access_point as ap
 
 from acts import asserts
 from acts import utils
 from acts.base_test import BaseTestClass
-from acts.signals import TestSignal
-from acts.controllers import android_device
 from acts.controllers.ap_lib import hostapd_ap_preset
 from acts.controllers.ap_lib import hostapd_bss_settings
 from acts.controllers.ap_lib import hostapd_constants
@@ -76,19 +69,19 @@
         ref_5g_passphrase = utils.rand_ascii_str(passphrase_length_5g)
 
         if hidden:
-           network_dict_2g = {
-              "SSID": ref_2g_ssid,
-              "security": ref_2g_security,
-              "password": ref_2g_passphrase,
-              "hiddenSSID": true
-           }
+            network_dict_2g = {
+                "SSID": ref_2g_ssid,
+                "security": ref_2g_security,
+                "password": ref_2g_passphrase,
+                "hiddenSSID": True
+            }
 
-           network_dict_5g = {
-              "SSID": ref_5g_ssid,
-              "security": ref_5g_security,
-              "password": ref_5g_passphrase,
-              "hiddenSSID": true
-           }
+            network_dict_5g = {
+                "SSID": ref_5g_ssid,
+                "security": ref_5g_security,
+                "password": ref_5g_passphrase,
+                "hiddenSSID": True
+            }
         else:
             network_dict_2g = {
                 "SSID": ref_2g_ssid,
@@ -105,10 +98,8 @@
         ap = 0
         for ap in range(ap_count):
             self.user_params["reference_networks"].append({
-                "2g":
-                copy.copy(network_dict_2g),
-                "5g":
-                copy.copy(network_dict_5g)
+                "2g": copy.copy(network_dict_2g),
+                "5g": copy.copy(network_dict_5g)
             })
         self.reference_networks = self.user_params["reference_networks"]
         return {"2g": network_dict_2g, "5g": network_dict_5g}
@@ -136,15 +127,15 @@
         open_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
         if hidden:
             network_dict_2g = {
-            "SSID": open_2g_ssid,
-            "security": 'none',
-            "hiddenSSID": true
+                "SSID": open_2g_ssid,
+                "security": 'none',
+                "hiddenSSID": True
             }
 
             network_dict_5g = {
-            "SSID": open_5g_ssid,
-            "security": 'none',
-            "hiddenSSID": true
+                "SSID": open_5g_ssid,
+                "security": 'none',
+                "hiddenSSID": True
             }
         else:
             network_dict_2g = {
@@ -166,21 +157,21 @@
         self.open_network = self.user_params["open_network"]
         return {"2g": network_dict_2g, "5g": network_dict_5g}
 
-    def populate_bssid(self, ap_instance, ap, networks_5g, networks_2g):
+    def populate_bssid(self, ap_instance, ap, networks_2g, networks_5g):
         """Get bssid for a given SSID and add it to the network dictionary.
 
         Args:
             ap_instance: Accesspoint index that was configured.
             ap: Accesspoint object corresponding to ap_instance.
-            networks_5g: List of 5g networks configured on the APs.
             networks_2g: List of 2g networks configured on the APs.
+            networks_5g: List of 5g networks configured on the APs.
 
         """
 
-        if not (networks_5g or networks_2g):
+        if not (networks_2g or networks_5g):
             return
 
-        for network in itertools.chain(networks_5g, networks_2g):
+        for network in itertools.chain(networks_2g, networks_5g):
             if 'channel' in network:
                 continue
             bssid = ap.get_bssid_from_ssid(network["SSID"])
@@ -189,7 +180,7 @@
             else:
                 band = hostapd_constants.BAND_5G
             if network["security"] == hostapd_constants.WPA2_STRING:
-                # TODO:(bamahadev) Change all occurances of reference_networks
+                # TODO:(bamahadev) Change all occurrences of reference_networks
                 # to wpa_networks.
                 self.reference_networks[ap_instance][band]["bssid"] = bssid
             else:
@@ -197,21 +188,14 @@
 
     def legacy_configure_ap_and_start(
             self,
-            channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
             channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
-            max_2g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_2G,
-            max_5g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_5G,
-            ap_ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
-            ap_passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
-            ap_ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
-            ap_passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G,
-            hidden=False,
+            channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
             ap_count=1):
         asserts.assert_true(
             len(self.user_params["AccessPoint"]) == 2,
-            "Exactly two access points must be specified. \
-             Each access point has 2 radios, one each for 2.4GHZ \
-             and 5GHz. A test can choose to use one or both APs.")
+            "Exactly two access points must be specified. "
+            "Each access point has 2 radios, one each for 2.4GHZ "
+            "and 5GHz. A test can choose to use one or both APs.")
         network_list_2g = []
         network_list_5g = []
         network_list_2g.append({"channel": channel_2g})
@@ -234,16 +218,17 @@
         orig_network_list_5g = copy.copy(network_list_5g)
         orig_network_list_2g = copy.copy(network_list_2g)
 
-        if len(network_list_5g) > 1:
-            self.config_5g = self._generate_legacy_ap_config(network_list_5g)
         if len(network_list_2g) > 1:
             self.config_2g = self._generate_legacy_ap_config(network_list_2g)
+        if len(network_list_5g) > 1:
+            self.config_5g = self._generate_legacy_ap_config(network_list_5g)
         ap = 0
         for ap in range(ap_count):
             self.access_points[ap].start_ap(self.config_2g)
             self.access_points[ap].start_ap(self.config_5g)
-            self.populate_bssid(ap, self.access_points[ap], orig_network_list_5g,
-                                orig_network_list_2g)
+            self.populate_bssid(ap, self.access_points[ap],
+                                orig_network_list_2g,
+                                orig_network_list_5g)
 
     def _generate_legacy_ap_config(self, network_list):
         bss_settings = []
diff --git a/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py b/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py
index 1e8f9d0..c928acf 100644
--- a/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py
+++ b/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py
@@ -56,6 +56,9 @@
       self.set_power_mode_parameters(ad)
       utils.set_regulatory_domain(ad, "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:
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 4be392e..05ba784 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
@@ -404,6 +404,18 @@
   device.adb.shell(
     "cmd wifiaware native_api set-power %s %s %d" % (mode, name, value))
 
+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.
diff --git a/acts/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py b/acts/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
index a477672..bff59e7 100644
--- a/acts/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
+++ b/acts/tests/google/ble/conn_oriented_chan/BleCoc2ConnTest.py
@@ -27,8 +27,10 @@
 from acts.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
 from acts.test_utils.bt.bt_coc_test_utils import do_multi_connection_throughput
 from acts.test_utils.bt.bt_constants import default_le_data_length
+from acts.test_utils.bt.bt_constants import default_bluetooth_socket_timeout_ms
 from acts.test_utils.bt.bt_constants import l2cap_coc_header_size
 from acts.test_utils.bt.bt_constants import l2cap_max_inactivity_delay_after_disconnect
+from acts.test_utils.bt.bt_constants import le_connection_event_time_step_ms
 from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
 from acts.test_utils.bt.bt_test_utils import kill_bluetooth_process
 from acts.test_utils.bt.bt_test_utils import reset_bluetooth
@@ -56,12 +58,22 @@
         # Give sufficient time for the physical LE link to be disconnected.
         time.sleep(l2cap_max_inactivity_delay_after_disconnect)
 
+    # This utility function calculates the max and min connection event (ce) time.
+    # The formula is that the min/max ce time should be less than half the connection
+    # interval and must be multiples of the le_connection_event_time_step.
+    def _calc_min_max_ce_time(self, le_connection_interval):
+        conn_event_time_steps = int((le_connection_interval/2)/le_connection_event_time_step_ms)
+        conn_event_time_steps -= 1
+        return (le_connection_event_time_step_ms * conn_event_time_steps)
+
     def _run_coc_connection_throughput_2_conn(
             self,
             is_secured,
             buffer_size,
             le_connection_interval=0,
-            le_tx_data_length=default_le_data_length):
+            le_tx_data_length=default_le_data_length,
+            min_ce_len=0,
+            max_ce_len=0):
 
         # The num_iterations is that number of repetitions of each
         # set of buffers r/w.
@@ -70,8 +82,6 @@
         # buffer_size is the number of bytes per L2CAP data buffer.
         num_iterations = 10
         number_buffers = 100
-        # Note: A 117 octets buffer size would fix nicely to a 123 bytes Data Length
-        buffer_size = 117
 
         # Make sure at least 3 phones are setup
         if len(self.android_devices) <= 2:
@@ -81,17 +91,19 @@
 
         self.log.info(
             "_run_coc_connection_throughput_2_conn: is_secured={}, Interval={}, buffer_size={}, "
-            "le_tx_data_length={}".format(is_secured, le_connection_interval,
-                                          buffer_size, le_tx_data_length))
+            "le_tx_data_length={}, min_ce_len={}".format(is_secured, le_connection_interval,
+                                          buffer_size, le_tx_data_length, min_ce_len))
         status, client_conn_id1, server_conn_id1 = orchestrate_coc_connection(
             self.client_ad, self.server_ad, True, is_secured,
-            le_connection_interval, le_tx_data_length)
+            le_connection_interval, le_tx_data_length, default_bluetooth_socket_timeout_ms,
+            min_ce_len, max_ce_len)
         if not status:
             return False
 
         status, client_conn_id2, server_conn_id2 = orchestrate_coc_connection(
             self.client_ad, self.server2_ad, True, is_secured,
-            le_connection_interval, le_tx_data_length)
+            le_connection_interval, le_tx_data_length, default_bluetooth_socket_timeout_ms,
+            min_ce_len, max_ce_len)
         if not status:
             return False
 
@@ -223,8 +235,11 @@
         le_connection_interval = 10
         buffer_size = 60
         le_tx_data_length = buffer_size + l2cap_coc_header_size
+
         status = self._run_coc_connection_throughput_2_conn(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+            is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+            self._calc_min_max_ce_time(le_connection_interval),
+            self._calc_min_max_ce_time(le_connection_interval))
         return status
 
     @BluetoothBaseTest.bt_test_wrap
@@ -266,7 +281,9 @@
         buffer_size = 80
         le_tx_data_length = buffer_size + l2cap_coc_header_size
         status = self._run_coc_connection_throughput_2_conn(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+            is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+            self._calc_min_max_ce_time(le_connection_interval),
+            self._calc_min_max_ce_time(le_connection_interval))
         return status
 
     @BluetoothBaseTest.bt_test_wrap
@@ -308,7 +325,9 @@
         buffer_size = 120
         le_tx_data_length = buffer_size + l2cap_coc_header_size
         status = self._run_coc_connection_throughput_2_conn(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+            is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+            self._calc_min_max_ce_time(le_connection_interval),
+            self._calc_min_max_ce_time(le_connection_interval))
         return status
 
     @BluetoothBaseTest.bt_test_wrap
@@ -350,7 +369,9 @@
         buffer_size = 120
         le_tx_data_length = buffer_size + l2cap_coc_header_size
         status = self._run_coc_connection_throughput_2_conn(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+            is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+            self._calc_min_max_ce_time(le_connection_interval),
+            self._calc_min_max_ce_time(le_connection_interval))
         return status
 
     @BluetoothBaseTest.bt_test_wrap
@@ -392,7 +413,9 @@
         buffer_size = 180
         le_tx_data_length = buffer_size + l2cap_coc_header_size
         status = self._run_coc_connection_throughput_2_conn(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+            is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+            self._calc_min_max_ce_time(le_connection_interval),
+            self._calc_min_max_ce_time(le_connection_interval))
         return status
 
     @BluetoothBaseTest.bt_test_wrap
@@ -434,7 +457,9 @@
         buffer_size = 240
         le_tx_data_length = buffer_size + l2cap_coc_header_size
         status = self._run_coc_connection_throughput_2_conn(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+            is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+            self._calc_min_max_ce_time(le_connection_interval),
+            self._calc_min_max_ce_time(le_connection_interval))
         return status
 
     @BluetoothBaseTest.bt_test_wrap
@@ -476,5 +501,7 @@
         buffer_size = 240
         le_tx_data_length = buffer_size + l2cap_coc_header_size
         status = self._run_coc_connection_throughput_2_conn(
-            is_secured, buffer_size, le_connection_interval, le_tx_data_length)
+            is_secured, buffer_size, le_connection_interval, le_tx_data_length,
+            self._calc_min_max_ce_time(le_connection_interval),
+            self._calc_min_max_ce_time(le_connection_interval))
         return status
diff --git a/acts/tests/google/bt/car_bt/BtCarPbapTest.py b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
index bc1b930..a3ae194 100644
--- a/acts/tests/google/bt/car_bt/BtCarPbapTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
@@ -1,4 +1,4 @@
-#/usr/bin/env python3.4
+# /usr/bin/env python3.4
 #
 # Copyright (C) 2016 The Android Open Source Project
 #
@@ -55,12 +55,10 @@
             "android.permission.WRITE_CONTACTS",
             "android.permission.READ_EXTERNAL_STORAGE"
         ]
-        for permission in permissions_list:
-            self.pse.adb.shell(
-                "pm grant com.google.android.contacts {}".format(permission))
-        for permission in permissions_list:
-            self.pce.adb.shell("pm grant com.android.contacts {}".format(
-                permission))
+        for device in [self.pce, self.pse, self.pse2]:
+            for permission in permissions_list:
+                device.adb.shell(
+                    "pm grant com.google.android.contacts {}".format(permission))
 
         # Pair the devices.
         # This call may block until some specified timeout in bt_test_utils.py.
@@ -115,6 +113,9 @@
     def teardown_test(self):
         if not super(BtCarPbapTest, self).teardown_test():
             return False
+        for device in [self.pce, self.pse, self.pse2]:
+            bt_contacts_utils.delete_vcf_files(device)
+
         self.pce.droid.bluetoothPbapClientDisconnect(
             self.pse.droid.bluetoothGetLocalAddress())
         bt_contacts_utils.erase_contacts(self.pse)
@@ -214,6 +215,8 @@
                                                 PSE_CONTACTS_FILE, 100)
         phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
             self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE)
+
+        bt_test_utils.reset_bluetooth([self.pce])
         bt_test_utils.connect_pri_to_sec(
             self.pce, self.pse,
             set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
@@ -249,6 +252,7 @@
                                                 PSE_CONTACTS_FILE, 100)
         phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
             self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE)
+        bt_test_utils.reset_bluetooth([self.pce])
         if not self.connect_and_verify(phone_numbers_added):
             return False
 
@@ -257,6 +261,7 @@
                                                 PSE_CONTACTS_FILE, 110, 2)
         phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
             self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE)
+        bt_test_utils.reset_bluetooth([self.pce])
         return self.connect_and_verify(phone_numbers_added)
 
     @test_tracker_info(uuid='bbe31bf5-51e8-4175-b266-1c7750e44f5b')
@@ -330,7 +335,7 @@
 
         phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
             self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE)
-
+        bt_test_utils.reset_bluetooth([self.pce])
         return self.connect_and_verify(phone_numbers_added)
 
     @test_tracker_info(uuid='2aa2bd00-86cc-4f39-a06a-90b17ea5b320')
@@ -432,13 +437,16 @@
 
         bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
                                                 PSE1_CONTACTS_FILE, 100)
-        phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
-            self.pse, self.contacts_destination_path, PSE1_CONTACTS_FILE)
+        phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(self.pse,
+                                                                                self.contacts_destination_path,
+                                                                                PSE1_CONTACTS_FILE)
         bt_contacts_utils.generate_contact_list(self.contacts_destination_path,
                                                 PSE2_CONTACTS_FILE, 100)
-        phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
-            self.pse2, self.contacts_destination_path, PSE2_CONTACTS_FILE)
+        phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(self.pse2,
+                                                                                self.contacts_destination_path,
+                                                                                PSE2_CONTACTS_FILE)
 
+        bt_test_utils.reset_bluetooth([self.pce])
         self.pce.droid.bluetoothPbapClientDisconnect(
             self.pse.droid.bluetoothGetLocalAddress())
         self.pce.droid.bluetoothPbapClientDisconnect(
diff --git a/acts/tests/google/wifi/WifiIOTTest.py b/acts/tests/google/wifi/WifiIOTTest.py
index 1f3da05..36f7302 100755
--- a/acts/tests/google/wifi/WifiIOTTest.py
+++ b/acts/tests/google/wifi/WifiIOTTest.py
@@ -43,17 +43,22 @@
         self.dut = self.android_devices[0]
         wutils.wifi_test_device_init(self.dut)
 
-        req_params = [ "iot_networks", "open_network", "iperf_server_address" ]
-        self.unpack_userparams(req_param_names=req_params)
+        req_params = [ "iot_networks", ]
+        opt_params = [ "open_network", "iperf_server_address" ]
+        self.unpack_userparams(req_param_names=req_params,
+                               opt_param_names=opt_params)
 
         asserts.assert_true(
             len(self.iot_networks) > 0,
             "Need at least one iot network with psk.")
-        self.iot_networks.append(self.open_network)
+
+        if self.open_network:
+            self.iot_networks.append(self.open_network)
 
         wutils.wifi_toggle_state(self.dut, True)
         if "iperf_server_address" in self.user_params:
             self.iperf_server = self.iperf_servers[0]
+            self.iperf_server.start()
 
         # create hashmap for testcase name and SSIDs
         self.iot_test_prefix = "test_iot_connection_to_"
@@ -61,7 +66,6 @@
         for network in self.iot_networks:
             SSID = network['SSID'].replace('-','_')
             self.ssid_map[SSID] = network
-        self.iperf_server.start()
 
     def setup_test(self):
         self.dut.droid.wakeLockAcquireBright()
@@ -73,7 +77,8 @@
         wutils.reset_wifi(self.dut)
 
     def teardown_class(self):
-        self.iperf_server.stop()
+        if "iperf_server_address" in self.user_params:
+            self.iperf_server.stop()
 
     def on_fail(self, test_name, begin_time):
         self.dut.take_bug_report(test_name, begin_time)
diff --git a/acts/tests/google/wifi/WifiRssiTest.py b/acts/tests/google/wifi/WifiRssiTest.py
index 73449d6..aa95b23 100644
--- a/acts/tests/google/wifi/WifiRssiTest.py
+++ b/acts/tests/google/wifi/WifiRssiTest.py
@@ -45,9 +45,10 @@
 
     def setup_class(self):
         self.dut = self.android_devices[0]
-        req_params = ["test_params", "main_network"]
+        req_params = ["rssi_test_params", "main_network"]
         opt_params = ["RetailAccessPoints"]
         self.unpack_userparams(req_params, opt_params)
+        self.test_params = self.rssi_test_params
         self.num_atten = self.attenuators[0].instrument.num_atten
         self.iperf_server = self.iperf_servers[0]
         self.access_points = retail_ap.create(self.RetailAccessPoints)
@@ -370,6 +371,7 @@
             "chain_1_rssi": []
         }
         for idx in range(num_measurements):
+            measurement_start_time = time.time()
             # Get signal poll RSSI
             signal_poll_output = self.dut.adb.shell(SIGNAL_POLL)
             match = re.search("RSSI=.*", signal_poll_output)
@@ -399,7 +401,9 @@
             else:
                 connected_rssi["chain_0_rssi"].append(RSSI_ERROR_VAL)
                 connected_rssi["chain_1_rssi"].append(RSSI_ERROR_VAL)
-            time.sleep(polling_frequency)
+            measurement_elapsed_time = time.time() - measurement_start_time
+            time.sleep(max(0, polling_frequency - measurement_elapsed_time))
+
         # Compute mean RSSIs. Only average valid readings.
         # Output RSSI_ERROR_VAL if no valid connected readings found.
         for key, val in connected_rssi.copy().items():
diff --git a/acts/tests/google/wifi/WifiRvrTest.py b/acts/tests/google/wifi/WifiRvrTest.py
index 1310667..f40cc50 100644
--- a/acts/tests/google/wifi/WifiRvrTest.py
+++ b/acts/tests/google/wifi/WifiRvrTest.py
@@ -39,11 +39,12 @@
 
     def setup_class(self):
         self.client_dut = self.android_devices[-1]
-        req_params = ["test_params"]
+        req_params = ["rvr_test_params"]
         opt_params = [
             "main_network", "RetailAccessPoints", "golden_files_list"
         ]
         self.unpack_userparams(req_params, opt_params)
+        self.test_params = self.rvr_test_params
         self.num_atten = self.attenuators[0].instrument.num_atten
         self.iperf_server = self.iperf_servers[0]
         if hasattr(self, "RetailAccessPoints"):
diff --git a/acts/tests/google/wifi/WifiThroughputStabilityTest.py b/acts/tests/google/wifi/WifiThroughputStabilityTest.py
index 3da256c..93a1b16 100644
--- a/acts/tests/google/wifi/WifiThroughputStabilityTest.py
+++ b/acts/tests/google/wifi/WifiThroughputStabilityTest.py
@@ -93,9 +93,10 @@
 
     def setup_class(self):
         self.dut = self.android_devices[0]
-        req_params = ["test_params", "main_network"]
+        req_params = ["throughput_stability_test_params", "main_network"]
         opt_params = ["RetailAccessPoints"]
         self.unpack_userparams(req_params, opt_params)
+        self.test_params = self.throughput_stability_test_params
         self.num_atten = self.attenuators[0].instrument.num_atten
         self.iperf_server = self.iperf_servers[0]
         self.access_points = retail_ap.create(self.RetailAccessPoints)
diff --git a/acts/tests/google/wifi/aware/functional/DataPathTest.py b/acts/tests/google/wifi/aware/functional/DataPathTest.py
index c3598bf..72ce3ef 100644
--- a/acts/tests/google/wifi/aware/functional/DataPathTest.py
+++ b/acts/tests/google/wifi/aware/functional/DataPathTest.py
@@ -958,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:
@@ -1234,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
@@ -1274,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:
@@ -1298,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)
 
@@ -1311,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)
@@ -1836,3 +1851,71 @@
     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/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/ThroughputTest.py b/acts/tests/google/wifi/aware/performance/ThroughputTest.py
index 7ee6e08..ddb6d15 100644
--- a/acts/tests/google/wifi/aware/performance/ThroughputTest.py
+++ b/acts/tests/google/wifi/aware/performance/ThroughputTest.py
@@ -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..28de5a7 100644
--- a/acts/tests/google/wifi/aware/stress/DataPathStressTest.py
+++ b/acts/tests/google/wifi/aware/stress/DataPathStressTest.py
@@ -37,9 +37,19 @@
 
   ################################################################
 
-  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 +60,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 +78,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(1)
+
+          # 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
@@ -144,3 +177,17 @@
         'test_oob_ndp_stress finished',
         extras=results)
     asserts.explicit_pass("test_oob_ndp_stress done", extras=results)
+
+  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)
+
+  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(1, 3, 0)