| #!/usr/bin/env python3 |
| # |
| # Copyright (C) 2016 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. |
| """Test script to test PBAP contact download between two devices which can run SL4A. |
| """ |
| |
| import os |
| import time |
| |
| from acts.test_decorators import test_tracker_info |
| from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest |
| from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test |
| from acts.base_test import BaseTestClass |
| from acts.test_utils.bt import bt_contacts_utils |
| from acts.test_utils.bt import bt_test_utils |
| from acts.test_utils.car import car_bt_utils |
| from acts.utils import exe_cmd |
| import acts.test_utils.bt.BtEnum as BtEnum |
| |
| # Offset call logs by 1 minute |
| CALL_LOG_TIME_OFFSET_IN_MSEC = 60000 |
| PSE_CONTACTS_FILE = "psecontacts.vcf" |
| PCE_CONTACTS_FILE = "pcecontacts.vcf" |
| MERGED_CONTACTS_FILE = "psecombined.vcf" |
| STANDART_CONTACT_COUNT = 100 |
| |
| |
| class BtCarPbapTest(BluetoothBaseTest): |
| contacts_destination_path = "" |
| |
| def setup_class(self): |
| if not super(BtCarPbapTest, self).setup_class(): |
| return False |
| self.pce = self.android_devices[0] |
| self.pse = self.android_devices[1] |
| self.pse2 = self.android_devices[2] |
| self.contacts_destination_path = self.log_path + "/" |
| |
| permissions_list = [ |
| "android.permission.READ_CONTACTS", |
| "android.permission.WRITE_CONTACTS", |
| "android.permission.READ_EXTERNAL_STORAGE" |
| ] |
| for device in self.android_devices: |
| 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. |
| # Grace time inbetween stack state changes |
| if not bt_test_utils.pair_pri_to_sec(self.pce, self.pse): |
| self.log.error("Failed to pair.") |
| return False |
| time.sleep(3) |
| if not bt_test_utils.pair_pri_to_sec(self.pce, self.pse2): |
| self.log.error("Failed to pair.") |
| return False |
| |
| # Disable the HFP and A2DP profiles. This will ensure only PBAP |
| # gets connected. Also, this will eliminate the auto-connect loop. |
| car_bt_utils.set_car_profile_priorities_off(self.pce, self.pse) |
| car_bt_utils.set_car_profile_priorities_off(self.pce, self.pse2) |
| |
| # Enable PBAP on PSE & PCE. |
| |
| self.pse.droid.bluetoothChangeProfileAccessPermission( |
| self.pce.droid.bluetoothGetLocalAddress(), |
| BtEnum.BluetoothProfile.PBAP_SERVER.value, |
| BtEnum.BluetoothAccessLevel.ACCESS_ALLOWED.value) |
| |
| self.pse2.droid.bluetoothChangeProfileAccessPermission( |
| self.pce.droid.bluetoothGetLocalAddress(), |
| BtEnum.BluetoothProfile.PBAP_SERVER.value, |
| BtEnum.BluetoothAccessLevel.ACCESS_ALLOWED.value) |
| |
| bt_test_utils.set_profile_priority( |
| self.pce, self.pse, [BtEnum.BluetoothProfile.PBAP_CLIENT], |
| BtEnum.BluetoothPriorityLevel.PRIORITY_ON) |
| bt_test_utils.set_profile_priority( |
| self.pce, self.pse2, [BtEnum.BluetoothProfile.PBAP_CLIENT], |
| BtEnum.BluetoothPriorityLevel.PRIORITY_ON) |
| |
| return True |
| |
| def setup_test(self): |
| if not super(BtCarPbapTest, self).setup_test(): |
| return False |
| self.pse.droid.callLogsEraseAll() |
| return self.erase_all_contacts() |
| |
| def teardown_test(self): |
| if not super(BtCarPbapTest, self).teardown_test(): |
| return False |
| for device in self.android_devices: |
| bt_contacts_utils.delete_vcf_files(device) |
| |
| self.pce.droid.bluetoothPbapClientDisconnect( |
| self.pse.droid.bluetoothGetLocalAddress()) |
| 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( |
| self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE) |
| return bt_contacts_utils.count_contacts_with_differences( |
| self.contacts_destination_path, PCE_CONTACTS_FILE, |
| PSE_CONTACTS_FILE) == 0 |
| |
| def connect_and_verify(self, count): |
| bt_test_utils.connect_pri_to_sec( |
| self.pce, self.pse, |
| 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 |
| |
| @test_tracker_info(uuid='7dcdecfc-42d1-4f41-b66e-823c8f161356') |
| @BluetoothBaseTest.bt_test_wrap |
| 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.pce, self.pse, |
| 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.pce, self.pse, |
| set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])): |
| self.log.error("No client connected and should be.") |
| return False |
| |
| return True |
| |
| @test_tracker_info(uuid='1733efb9-71af-4956-bd3a-0d3167d94d0c') |
| @BluetoothBaseTest.bt_test_wrap |
| def test_contact_download(self): |
| """Test Contact Download |
| |
| Test download of contacts from a clean state. |
| |
| Precondition: |
| 1. Devices are paired. |
| |
| 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 |
| """ |
| bt_contacts_utils.generate_contact_list(self.contacts_destination_path, |
| 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.connect_pri_to_sec( |
| self.pce, self.pse, |
| 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 |
| return bt_contacts_utils.erase_contacts(self.pce) |
| |
| @test_tracker_info(uuid='99dc6ac6-b7cf-45ce-927b-8c4ebf8ab664') |
| @BluetoothBaseTest.bt_test_wrap |
| 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(self.contacts_destination_path, |
| PSE_CONTACTS_FILE, 100) |
| phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf( |
| self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE) |
| if not self.connect_and_verify(phone_numbers_added): |
| return False |
| |
| bt_contacts_utils.erase_contacts(self.pse) |
| bt_contacts_utils.generate_contact_list(self.contacts_destination_path, |
| 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) |
| return self.connect_and_verify(phone_numbers_added) |
| |
| @test_tracker_info(uuid='bbe31bf5-51e8-4175-b266-1c7750e44f5b') |
| @BluetoothBaseTest.bt_test_wrap |
| 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( |
| self.contacts_destination_path, PSE_CONTACTS_FILE, vcards) |
| |
| phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf( |
| self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE) |
| return self.connect_and_verify(phone_numbers_added) |
| |
| @test_tracker_info(uuid='2aa2bd00-86cc-4f39-a06a-90b17ea5b320') |
| @BluetoothBaseTest.bt_test_wrap |
| 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.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.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.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.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.time()) * 1000 - 2 * CALL_LOG_TIME_OFFSET_IN_MSEC) |
| |
| self.pce.droid.bluetoothPbapClientDisconnect( |
| self.pse.droid.bluetoothGetLocalAddress()) |
| self.pce.droid.bluetoothPbapClientDisconnect( |
| self.pse2.droid.bluetoothGetLocalAddress()) |
| |
| bt_test_utils.connect_pri_to_sec( |
| self.pce, self.pse, |
| 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 |
| |
| @test_tracker_info(uuid='bb018bf4-5a61-478d-acce-eef88050e489') |
| @BluetoothBaseTest.bt_test_wrap |
| def test_multiple_phones(self): |
| """Test Multiple Phones |
| |
| Test that connects two phones and confirms contacts are transfered |
| and merged while still being associated with their original phone. |
| |
| Precondition: |
| 1. Devices are paired. |
| |
| Steps: |
| 1. Add a unique list of contacts to PSE on each phone. |
| 2. Connect PCE to PSE 1 to perform transfer. |
| 3. Verify contacts match. |
| 4. Connect PCE to PSE 2 to perform transfer. |
| 5. Verify that the PCE has a union set of contacts from |
| PSE 1 and PSE 2. |
| 6. Disconnect PCE from PSE 1 to clean up contacts. |
| 7. Verify that only PSE 2 contacts remain on PCE and they match. |
| 8. Disconnect PCE from PSE 2 to clean up contacts. |
| |
| Returns: |
| 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) |
| 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) |
| bt_contacts_utils.import_device_contacts_from_vcf(self.pse2, |
| self.contacts_destination_path, |
| PSE2_CONTACTS_FILE) |
| |
| self.pce.droid.bluetoothPbapClientDisconnect( |
| self.pse.droid.bluetoothGetLocalAddress()) |
| self.pce.droid.bluetoothPbapClientDisconnect( |
| self.pse2.droid.bluetoothGetLocalAddress()) |
| |
| bt_test_utils.connect_pri_to_sec( |
| self.pce, self.pse, |
| set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])) |
| bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 100) |
| bt_contacts_utils.export_device_contacts_to_vcf( |
| self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE) |
| pse1_matches = bt_contacts_utils.count_contacts_with_differences( |
| self.contacts_destination_path, PCE_CONTACTS_FILE, |
| PSE1_CONTACTS_FILE) == 0 |
| |
| 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) |
| |
| merged_file = open('{}{}'.format(self.contacts_destination_path, |
| MERGED_CONTACTS_FILE), 'w') |
| for contacts_file in [PSE1_CONTACTS_FILE, PSE2_CONTACTS_FILE]: |
| infile = open(self.contacts_destination_path + contacts_file) |
| merged_file.write(infile.read()) |
| |
| self.log.info("Checking combined phonebook.") |
| pse1andpse2_matches = bt_contacts_utils.count_contacts_with_differences( |
| self.contacts_destination_path, PCE_CONTACTS_FILE, |
| MERGED_CONTACTS_FILE) == 0 |
| |
| self.pce.droid.bluetoothPbapClientDisconnect( |
| self.pse.droid.bluetoothGetLocalAddress()) |
| bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 100) |
| |
| self.log.info("Checking phonebook after disconnecting first device.") |
| bt_contacts_utils.export_device_contacts_to_vcf( |
| self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE) |
| pse2_matches = bt_contacts_utils.count_contacts_with_differences( |
| self.contacts_destination_path, PCE_CONTACTS_FILE, |
| PSE2_CONTACTS_FILE) == 0 |
| |
| bt_contacts_utils.erase_contacts(self.pse) |
| bt_contacts_utils.erase_contacts(self.pse2) |
| return pse1_matches and pse2_matches and pse1andpse2_matches |
| |