SL4A testing for PBAP
Extend SL4A testing to include more extensive testing for PBAP PCE.
Test cases were added to automate test cases in
https://testtracker.googleplex.com/testplans/details/3975
for connectivity, contact transfer, call log transfer, and some edge
cases. Tests currently covered are 1.7.1-9, 1.7.11-15,
and 1.7.21-22
bug: 29100950
Change-Id: I5f59ddc7504996d33c709d48a7f2e8ebbb52bd17
diff --git a/acts/tests/google/bt/car_bt/BtCarPbapTest.py b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
index ac94738..1ee2c5c 100644
--- a/acts/tests/google/bt/car_bt/BtCarPbapTest.py
+++ b/acts/tests/google/bt/car_bt/BtCarPbapTest.py
@@ -14,31 +14,24 @@
# License for the specific language governing permissions and limitations under
# the License.
"""Test script to test PBAP contact download between two devices which can run SL4A.
-
-The script does the following:
- Setup:
- Clear up the bonded devices on both bluetooth adapters and bond
- the DUTs to each other.
"""
-from os.path import exists
+from time import sleep
+from time import time
from acts.base_test import BaseTestClass
-from acts.test_utils.bt.bt_test_utils import pair_pri_to_sec
-from acts.test_utils.bt.bt_test_utils import reset_bluetooth
-from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
+from acts.test_utils.bt import bt_test_utils
from acts.utils import exe_cmd
import acts.test_utils.bt.bt_contacts_utils as bt_contacts_utils
+import acts.test_utils.bt.BtEnum as BtEnum
-# PBAP Server is profile 6
-PBAP_SERVER_PROFILE = 6
-# Access Levels from BluetoothDevice
-ACCESS_ALLOWED = 1
-
-#file names for contacts cards
+# Names for temporary files for contacts cards for import and export from PSE and PCE
PSE_CONTACTS_FILE = "psecontacts.vcf"
PCE_CONTACTS_FILE = "pcecontacts.vcf"
-PCE_CONTACTS_ON_DEVICE= "/sdcard/Download/pcecontacts.vcf"
+
+# Offset call logs by 1 minute
+CALL_LOG_TIME_OFFSET_IN_MSEC = 60000
+
class BtCarPbapTest(BaseTestClass):
def __init__(self, controllers):
@@ -48,68 +41,310 @@
def setup_class(self):
# Reset the devices in a clean state.
- reset_bluetooth(self.android_devices)
+ bt_test_utils.reset_bluetooth(self.android_devices)
for a in self.android_devices:
a.ed.clear_all_events()
# Pair the devices.
# This call may block until some specified timeout in bt_test_utils.py.
- if not pair_pri_to_sec(self.pce.droid, self.pse.droid):
- self.log.error("pair_pri_to_sec returned false.")
+ if not bt_test_utils.pair_pri_to_sec(self.pce.droid, self.pse.droid):
+ self.log.error("Failed to pair.")
return False
- self.pse.droid.bluetoothChangeProfileAccessPermission(
- self.pse.droid.bluetoothGetBondedDevices()[0]["address"],
- PBAP_SERVER_PROFILE, ACCESS_ALLOWED)
+ self.pse.droid.bluetoothChangeProfileAccessPermission(
+ self.pce.droid.bluetoothGetLocalAddress(),
+ BtEnum.BluetoothProfile.PBAP_SERVER.value,
+ BtEnum.BluetoothAccessLevel.ACCESS_ALLOWED.value)
+
+ # Allow Autoconnect to process so we can start disconnected.
+ sleep(15)
+
+ self.pce.droid.bluetoothPbapClientDisconnect(
+ self.pse.droid.bluetoothGetLocalAddress())
+ if self.pce.droid.bluetoothGetConnectedDevicesOnProfile(
+ BtEnum.BluetoothProfile.PBAP_CLIENT.value):
+ self.log.error("Client connected and shouldn't be.")
+ return False
return True
def setup_test(self):
bt_contacts_utils.set_logger(self.log)
- if not exists(PSE_CONTACTS_FILE):
- self.log.error("ERROR, file {} does not exist in cwd"
- .format(PSE_CONTACTS_FILE))
+ 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.
+ sleep(1)
return True
def teardown_test(self):
+ self.pce.droid.bluetoothPbapClientDisconnect(
+ self.pse.droid.bluetoothGetLocalAddress())
+ bt_contacts_utils.erase_contacts(self.pse)
return True
def on_fail(self, test_name, begin_time):
- take_btsnoop_logs(self.android_devices, self, test_name)
+ bt_test_utils.take_btsnoop_logs(self.android_devices, self, test_name)
- def _verify_contacts_match(self):
- bt_contacts_utils.pull_contacts(self.pce, PCE_CONTACTS_FILE)
- return bt_contacts_utils.compare_contacts(PCE_CONTACTS_FILE,
- PSE_CONTACTS_FILE) == 0
+ def verify_contacts_match(self):
+ bt_contacts_utils.export_device_contacts_to_vcf(self.pce,
+ PCE_CONTACTS_FILE)
+ return bt_contacts_utils.count_contacts_with_differences(
+ PCE_CONTACTS_FILE, PSE_CONTACTS_FILE) == 0
+
+ def connect_and_verify(self, count):
+ bt_test_utils.connect_pri_to_sec(
+ self.log, self.pce, self.pse.droid,
+ set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
+ bt_contacts_utils.wait_for_phone_number_update_complete(self.pce,
+ count)
+ contacts_added = self.verify_contacts_match()
+ self.pce.droid.bluetoothPbapClientDisconnect(
+ self.pse.droid.bluetoothGetLocalAddress())
+ contacts_removed = bt_contacts_utils.wait_for_phone_number_update_complete(
+ self.pce, 0)
+ return contacts_added and contacts_removed
+
+ def test_pbap_connect_and_disconnect(self):
+ """Test Connectivity
+
+ Test connecting with the server enabled and disabled
+
+ Precondition:
+ 1. Devices are paired.
+
+ Steps:
+ 1. Disable permission on PSE to prevent PCE from connecting
+ 2. Attempt to connect PCE to PSE
+ 3. Verify connection failed
+ 4. Enable permission on PSE to allow PCE to connect
+ 5. Attempt to connect PCE to PSE
+ 6. Verify connection succeeded
+
+ Returns:
+ Pass if True
+ Fail if False
+ """
+ self.pse.droid.bluetoothChangeProfileAccessPermission(
+ self.pce.droid.bluetoothGetLocalAddress(),
+ BtEnum.BluetoothProfile.PBAP_SERVER.value,
+ BtEnum.BluetoothAccessLevel.ACCESS_DENIED.value)
+ if bt_test_utils.connect_pri_to_sec(
+ self.log, self.pce, self.pse.droid,
+ set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])):
+ self.log.error("Client connected and shouldn't be.")
+ return False
+ self.pce.droid.bluetoothPbapClientDisconnect(
+ self.pse.droid.bluetoothGetLocalAddress())
+
+ self.pse.droid.bluetoothChangeProfileAccessPermission(
+ self.pce.droid.bluetoothGetLocalAddress(),
+ BtEnum.BluetoothProfile.PBAP_SERVER.value,
+ BtEnum.BluetoothAccessLevel.ACCESS_ALLOWED.value)
+ if not bt_test_utils.connect_pri_to_sec(
+ self.log, self.pce, self.pse.droid,
+ set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])):
+ self.log.error("No client connected and should be.")
+ return False
+
+ return True
def test_contact_download(self):
"""Test Contact Download
Test download of contacts from a clean state.
- 1. Erase contacts from PSE.
- 2. Add a predefined list of contacts to PSE.
- 3. Connect PCE to PSE to perform transfer.
- 4. Compare transfered contacts.
- 5. Disconnect.
- 6. Verify PCE cleaned up contact list.
+ Precondition:
+ 1. Devices are paired.
- Returns:
- Pass if True
- Fail if False
+ Steps:
+ 1. Erase contacts from PSE and PCE.
+ 2. Add a predefined list of contacts to PSE.
+ 3. Connect PCE to PSE to perform transfer.
+ 4. Compare transfered contacts.
+ 5. Disconnect.
+ 6. Verify PCE cleaned up contact list.
+
+ Returns:
+ Pass if True
+ Fail if False
"""
- passing = True
- passing &= bt_contacts_utils.erase_contacts(self.pse)
- passing &= bt_contacts_utils.erase_contacts(self.pce)
- bt_contacts_utils.add_contacts(self.pse, PSE_CONTACTS_FILE)
- self.pce.droid.bluetoothPbapClientConnect(
- self.pse.droid.bluetoothGetLocalAddress())
- bt_contacts_utils.wait_for_contact_update_complete(self.pce, 100)
- passing &= self._verify_contacts_match()
+ bt_contacts_utils.generate_contact_list(PSE_CONTACTS_FILE, 100)
+ phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
+ self.pse, PSE_CONTACTS_FILE)
+ bt_test_utils.connect_pri_to_sec(
+ self.log, self.pce, self.pse.droid,
+ set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
+ bt_contacts_utils.wait_for_phone_number_update_complete(
+ self.pce, phone_numbers_added)
+ if not self.verify_contacts_match():
+ return False
self.pce.droid.bluetoothPbapClientDisconnect(
self.pse.droid.bluetoothGetLocalAddress())
- passing &= bt_contacts_utils.wait_for_contact_update_complete(self.pce,
- 0)
+ return bt_contacts_utils.wait_for_phone_number_update_complete(
+ self.pce, 0)
+
+ def test_modify_phonebook(self):
+ """Test Modify Phonebook
+
+ Test changing contacts and reconnecting PBAP.
+
+ Precondition:
+ 1. Devices are paired.
+
+ Steps:
+ 1. Add a predefined list of contacts to PSE.
+ 2. Connect PCE to PSE to perform transfer.
+ 3. Verify that contacts match.
+ 4. Change some contacts on the PSE.
+ 5. Reconnect PCE to PSE to perform transfer.
+ 6. Verify that new contacts match.
+
+ Returns:
+ Pass if True
+ Fail if False
+ """
+ bt_contacts_utils.generate_contact_list(PSE_CONTACTS_FILE, 100)
+ phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
+ self.pse, PSE_CONTACTS_FILE)
+ if not self.connect_and_verify(phone_numbers_added):
+ return False
+
bt_contacts_utils.erase_contacts(self.pse)
- return passing
+ bt_contacts_utils.generate_contact_list(PSE_CONTACTS_FILE, 110, 2)
+ phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
+ self.pse, PSE_CONTACTS_FILE)
+ return self.connect_and_verify(phone_numbers_added)
+
+ def test_special_contacts(self):
+ """Test Special Contacts
+
+ Test numerous special cases of contacts that could cause errors.
+
+ Precondition:
+ 1. Devices are paired.
+
+ Steps:
+ 1. Add a predefined list of contacts to PSE that includes special cases:
+ 2. Connect PCE to PSE to perform transfer.
+ 3. Verify that contacts match.
+
+ Returns:
+ Pass if True
+ Fail if False
+ """
+
+ vcards = []
+
+ # Generate a contact with no email address
+ current_contact = bt_contacts_utils.VCard()
+ current_contact.first_name = "Mr."
+ current_contact.last_name = "Smiley"
+ current_contact.add_phone_number(
+ bt_contacts_utils.generate_random_phone_number())
+ vcards.append(current_contact)
+
+ # Generate a 2nd contact with the same name but different phone number
+ current_contact = bt_contacts_utils.VCard()
+ current_contact.first_name = "Mr."
+ current_contact.last_name = "Smiley"
+ current_contact.add_phone_number(
+ bt_contacts_utils.generate_random_phone_number())
+ vcards.append(current_contact)
+
+ # Generate a contact with no name
+ current_contact = bt_contacts_utils.VCard()
+ current_contact.email = "{}@gmail.com".format(
+ bt_contacts_utils.generate_random_string())
+ current_contact.add_phone_number(
+ bt_contacts_utils.generate_random_phone_number())
+ vcards.append(current_contact)
+
+ # Generate a contact with random characters in its name
+ current_contact = bt_contacts_utils.VCard()
+ current_contact.first_name = bt_contacts_utils.generate_random_string()
+ current_contact.last_name = bt_contacts_utils.generate_random_string()
+ current_contact.add_phone_number(
+ bt_contacts_utils.generate_random_phone_number())
+ vcards.append(current_contact)
+
+ # Generate a contact with only a phone number
+ current_contact = bt_contacts_utils.VCard()
+ current_contact.add_phone_number(
+ bt_contacts_utils.generate_random_phone_number())
+ vcards.append(current_contact)
+
+ # Generate a 2nd contact with only a phone number
+ current_contact = bt_contacts_utils.VCard()
+ current_contact.add_phone_number(
+ bt_contacts_utils.generate_random_phone_number())
+ vcards.append(current_contact)
+
+ bt_contacts_utils.create_new_contacts_vcf_from_vcards(
+ PSE_CONTACTS_FILE, vcards)
+
+ phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf(
+ self.pse, PSE_CONTACTS_FILE)
+
+ return self.connect_and_verify(phone_numbers_added)
+
+ def test_call_log(self):
+ """Test Call Log
+
+ Test that Call Logs are transfered
+
+ Precondition:
+ 1. Devices are paired.
+
+ Steps:
+ 1. Add a predefined list of calls to the PSE call log.
+ 2. Connect PCE to PSE to allow call log transfer
+ 3. Verify the Missed, Incoming, and Outgoing Call History
+
+ Returns:
+ 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,
+ int(time()) * 1000)
+ bt_contacts_utils.add_call_log(
+ self.pse, bt_contacts_utils.INCOMMING_CALL_TYPE,
+ bt_contacts_utils.generate_random_phone_number().phone_number,
+ int(time()) * 1000 - 4 * CALL_LOG_TIME_OFFSET_IN_MSEC)
+ bt_contacts_utils.add_call_log(
+ self.pse, bt_contacts_utils.OUTGOING_CALL_TYPE,
+ bt_contacts_utils.generate_random_phone_number().phone_number,
+ int(time()) * 1000 - CALL_LOG_TIME_OFFSET_IN_MSEC)
+ bt_contacts_utils.add_call_log(
+ self.pse, bt_contacts_utils.MISSED_CALL_TYPE,
+ bt_contacts_utils.generate_random_phone_number().phone_number,
+ int(time()) * 1000 - 2 * CALL_LOG_TIME_OFFSET_IN_MSEC)
+ bt_contacts_utils.add_call_log(
+ self.pse, bt_contacts_utils.MISSED_CALL_TYPE,
+ bt_contacts_utils.generate_random_phone_number().phone_number,
+ int(time()) * 1000 - 2 * CALL_LOG_TIME_OFFSET_IN_MSEC)
+
+ bt_test_utils.connect_pri_to_sec(
+ self.log, self.pce, self.pse.droid,
+ set([BtEnum.BluetoothProfile.PBAP_CLIENT.value]))
+ pse_call_log_count = self.pse.droid.callLogGetCount()
+ self.log.info("Waiting for {} call logs to be transfered".format(
+ pse_call_log_count))
+ bt_contacts_utils.wait_for_call_log_update_complete(self.pce,
+ pse_call_log_count)
+
+ if not bt_contacts_utils.get_and_compare_call_logs(
+ self.pse, self.pce, bt_contacts_utils.INCOMMING_CALL_TYPE):
+ return False
+ if not bt_contacts_utils.get_and_compare_call_logs(
+ self.pse, self.pce, bt_contacts_utils.OUTGOING_CALL_TYPE):
+ return False
+ if not bt_contacts_utils.get_and_compare_call_logs(
+ self.pse, self.pce, bt_contacts_utils.MISSED_CALL_TYPE):
+ return False
+
+ return True