Merge "[RTT] Validate RTT results parameters" into pi-dev
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 91d37cc..70eac5e 100644
--- a/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_contacts_utils.py
@@ -24,7 +24,7 @@
 import re
 import random
 import string
-from time import sleep
+import time
 from acts.utils import exe_cmd
 import queue
 
@@ -274,7 +274,7 @@
     return len(contact_list)
 
 
-def import_device_contacts_from_vcf(device, destination_path, vcf_file):
+def import_device_contacts_from_vcf(device, destination_path, vcf_file, timeout=10):
     """Uploads and import vcf file to device.
     """
     number_count = phone_number_count(destination_path, vcf_file)
@@ -283,10 +283,15 @@
     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))
+    start_time = time.time()
+    while time.time() < start_time + timeout:
+        #TODO: use unattended way to bypass contact import module instead of keyevent
+        if "ImportVCardActivity" in device.get_my_current_focus_window():
+            # keyevent to allow contacts import from vcf file
+            for key in ["DPAD_RIGHT", "DPAD_RIGHT", "ENTER"]:
+                device.adb.shell("input keyevent KEYCODE_{}".format(key))
+            break
+        time.sleep(1)
     if wait_for_phone_number_update_complete(device, number_count):
         return number_count
     else:
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 176da31..5ddc9a3 100644
--- a/acts/framework/acts/test_utils/bt/bt_test_utils.py
+++ b/acts/framework/acts/test_utils/bt/bt_test_utils.py
@@ -222,7 +222,7 @@
     threads = []
     try:
         for a in android_devices:
-            thread = threading.Thread(target=reset_bluetooth, args=([[a]]))
+            thread = threading.Thread(target=factory_reset_bluetooth, args=([[a]]))
             threads.append(thread)
             thread.start()
         for t in threads:
@@ -281,6 +281,63 @@
             return False
     return True
 
+def wait_for_bluetooth_manager_state(droid, state=None, timeout=10, threshold=5):
+    """ Waits for BlueTooth normalized state or normalized explicit state
+    args:
+        droid: droid device object
+        state: expected BlueTooth state
+        timeout: max timeout threshold
+        threshold: list len of bt state
+    Returns:
+        True if successful, false if unsuccessful.
+    """
+    all_states = []
+    get_state = lambda: droid.bluetoothGetLeState()
+    start_time = time.time()
+    while time.time() < start_time + timeout:
+        all_states.append(get_state())
+        if len(all_states) >= threshold:
+            # for any normalized state
+            if state is None:
+                if len(set(all_states[-threshold:])) == 1:
+                    log.info("State normalized {}".format(set(all_states[-threshold:])))
+                    return True
+            else:
+                # explicit check against normalized state
+                if set([state]).issubset(all_states[-threshold:]):
+                    return True
+        time.sleep(0.5)
+    log.error(
+        "Bluetooth state fails to normalize" if state is None else
+        "Failed to match bluetooth state, current state {} expected state {}".format(get_state(), state))
+    return False
+
+def factory_reset_bluetooth(android_devices):
+    """Clears Bluetooth stack of input Android device list.
+
+        Args:
+            android_devices: The Android device list to reset Bluetooth
+
+        Returns:
+            True if successful, false if unsuccessful.
+        """
+    for a in android_devices:
+        droid, ed = a.droid, a.ed
+        a.log.info("Reset state of bluetooth on device.")
+        if not bluetooth_enabled_check(a):
+            return False
+        # TODO: remove device unbond b/79418045
+        # Temporary solution to ensure all devices are unbonded
+        bonded_devices = droid.bluetoothGetBondedDevices()
+        for b in bonded_devices:
+            a.log.info("Removing bond for device {}".format(b['address']))
+            droid.bluetoothUnbond(b['address'])
+
+        droid.bluetoothFactoryReset()
+        wait_for_bluetooth_manager_state(droid)
+        if not enable_bluetooth(droid, ed):
+            return False
+    return True
 
 def reset_bluetooth(android_devices):
     """Resets Bluetooth state of input Android device list.
@@ -519,7 +576,6 @@
     scan_ad.droid.bleStopBleScan(scan_callback)
     return mac_address, advertise_callback
 
-
 def enable_bluetooth(droid, ed):
     if droid.bluetoothCheckState() is True:
         return True
@@ -538,7 +594,6 @@
 
     return True
 
-
 def disable_bluetooth(droid):
     """Disable Bluetooth on input Droid object.
 
diff --git a/acts/tests/google/ble/scan/BleBackgroundScanTest.py b/acts/tests/google/ble/scan/BleBackgroundScanTest.py
index 3cb2842..9a20ed6 100644
--- a/acts/tests/google/ble/scan/BleBackgroundScanTest.py
+++ b/acts/tests/google/ble/scan/BleBackgroundScanTest.py
@@ -100,8 +100,8 @@
         """
         self.scn_ad.droid.bluetoothEnableBLE()
         self._setup_generic_advertisement()
-        self.scn_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
-            'low_latency'])
+        self.scn_ad.droid.bleSetScanSettingsScanMode(
+            ble_scan_settings_modes['low_latency'])
         filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
             self.scn_ad.droid)
         self.scn_ad.droid.bleSetScanFilterDeviceName(
@@ -152,8 +152,8 @@
         """
         self._setup_generic_advertisement()
         self.scn_ad.droid.bluetoothEnableBLE()
-        self.scn_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
-            'low_latency'])
+        self.scn_ad.droid.bleSetScanSettingsScanMode(
+            ble_scan_settings_modes['low_latency'])
         filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
             self.scn_ad.droid)
         self.scn_ad.droid.bleSetScanFilterDeviceName(
@@ -174,8 +174,9 @@
             try:
                 self.scn_ad.ed.pop_event(expected_event, self.default_timeout)
             except Empty:
-                self.log.error("Scan Result event not found. Expected {}".
-                               format(expected_event))
+                self.log.error(
+                    "Scan Result event not found. Expected {}".format(
+                        expected_event))
                 return False
         except Exception:
             self.log.info(
@@ -213,6 +214,7 @@
         """
         ble_state_error_msg = "Bluetooth LE State not OK {}. Expected {} got {}"
         # Enable BLE always available (effectively enabling BT in location)
+        self.scn_ad.shell.enable_ble_scanning()
         self.scn_ad.droid.bluetoothEnableBLE()
         self.scn_ad.droid.bluetoothToggleState(False)
         try:
@@ -237,8 +239,9 @@
         try:
             self.scn_ad.ed.pop_event(bluetooth_le_off, self.default_timeout)
         except Empty:
-            self.log.error("Bluetooth LE Off event not found. Expected {}".
-                           format(bluetooth_le_off))
+            self.log.error(
+                "Bluetooth LE Off event not found. Expected {}".format(
+                    bluetooth_le_off))
             return False
         state = self.scn_ad.droid.bluetoothGetLeState()
         if state != bt_adapter_states['off']:
diff --git a/acts/tests/google/bt/car_bt/BtCarPairingTest.py b/acts/tests/google/bt/car_bt/BtCarPairingTest.py
index 09810d1..f28ca83 100644
--- a/acts/tests/google/bt/car_bt/BtCarPairingTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarPairingTest.py
@@ -38,6 +38,12 @@
         self.car = self.android_devices[0]
         self.ph = self.android_devices[1]
 
+    def teardown_test(self):
+        for ad in self.android_devices:
+            bt_test_utils.clear_bonded_devices(ad)
+        # Give the stack time to unbond.
+        time.sleep(UNBOND_TIMEOUT)
+
     @test_tracker_info(uuid='f56e915-eef7-45cd-b5a6-771f6ef72602')
     @BluetoothBaseTest.bt_test_wrap
     def test_simple_pairing(self):
diff --git a/acts/tests/google/bt/car_bt/BtCarPbapTest.py b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
index a3ae194..9d195bc 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,7 +55,7 @@
             "android.permission.WRITE_CONTACTS",
             "android.permission.READ_EXTERNAL_STORAGE"
         ]
-        for device in [self.pce, self.pse, self.pse2]:
+        for device in self.android_devices:
             for permission in permissions_list:
                 device.adb.shell(
                     "pm grant com.google.android.contacts {}".format(permission))
@@ -103,23 +103,24 @@
         if not super(BtCarPbapTest, self).setup_test():
             return False
         self.pse.droid.callLogsEraseAll()
-        if not (bt_contacts_utils.erase_contacts(self.pse) and
-                bt_contacts_utils.erase_contacts(self.pce)):
-            return False
-        # Allow all content providers to synchronize.
-        time.sleep(1)
-        return True
+        return self.erase_all_contacts()
 
     def teardown_test(self):
         if not super(BtCarPbapTest, self).teardown_test():
             return False
-        for device in [self.pce, self.pse, self.pse2]:
+        for device in self.android_devices:
             bt_contacts_utils.delete_vcf_files(device)
 
         self.pce.droid.bluetoothPbapClientDisconnect(
             self.pse.droid.bluetoothGetLocalAddress())
-        bt_contacts_utils.erase_contacts(self.pse)
-        return True
+        return self.erase_all_contacts()
+
+    def erase_all_contacts(self):
+        try:
+            return all(bt_contacts_utils.erase_contacts(device) for device in self.android_devices)
+        finally:
+            # Allow all content providers to synchronize.
+            time.sleep(1)
 
     def verify_contacts_match(self):
         bt_contacts_utils.export_device_contacts_to_vcf(
@@ -215,8 +216,6 @@
                                                 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]))
@@ -252,7 +251,6 @@
                                                 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
 
@@ -261,7 +259,6 @@
                                                 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')
@@ -335,7 +332,6 @@
 
         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')
@@ -357,7 +353,6 @@
             Pass if True
             Fail if False
         """
-
         bt_contacts_utils.add_call_log(
             self.pse, bt_contacts_utils.INCOMMING_CALL_TYPE,
             bt_contacts_utils.generate_random_phone_number().phone_number,
@@ -431,22 +426,20 @@
            Pass if True
            Fail if False
         """
-
         PSE1_CONTACTS_FILE = "{}{}".format(PSE_CONTACTS_FILE, "1")
         PSE2_CONTACTS_FILE = "{}{}".format(PSE_CONTACTS_FILE, "2")
 
         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)
+        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)
+        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(
@@ -465,9 +458,7 @@
         bt_test_utils.connect_pri_to_sec(
             self.pce, self.pse2,
             set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
-
         bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 200)
-
         bt_contacts_utils.export_device_contacts_to_vcf(
             self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE)
 
@@ -495,4 +486,4 @@
 
         bt_contacts_utils.erase_contacts(self.pse)
         bt_contacts_utils.erase_contacts(self.pse2)
-        return pse1_matches and pse2_matches and pse1andpse2_matches
+        return pse1_matches and pse2_matches and pse1andpse2_matches
\ No newline at end of file