1 #/usr/bin/env python3.4 2 # 3 # Copyright (C) 2016 The Android Open Source Project 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 # use this file except in compliance with the License. You may obtain a copy of 7 # the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 # License for the specific language governing permissions and limitations under 15 # the License. 16 """Test script to test PBAP contact download between two devices which can run SL4A. 17 """ 18 19 import os 20 import time 21 22 from acts.test_decorators import test_tracker_info 23 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest 24 from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test 25 from acts.base_test import BaseTestClass 26 from acts.test_utils.bt import bt_contacts_utils 27 from acts.test_utils.bt import bt_test_utils 28 from acts.test_utils.car import car_bt_utils 29 from acts.utils import exe_cmd 30 import acts.test_utils.bt.BtEnum as BtEnum 31 32 # Offset call logs by 1 minute 33 CALL_LOG_TIME_OFFSET_IN_MSEC = 60000 34 PSE_CONTACTS_FILE = "psecontacts.vcf" 35 PCE_CONTACTS_FILE = "pcecontacts.vcf" 36 MERGED_CONTACTS_FILE = "psecombined.vcf" 37 STANDART_CONTACT_COUNT = 100 38 39 40 class BtCarPbapTest(BluetoothBaseTest): 41 contacts_destination_path = "" 42 43 def __init__(self, controllers): 44 BaseTestClass.__init__(self, controllers) 45 self.pce = self.android_devices[0] 46 self.pse = self.android_devices[1] 47 self.pse2 = self.android_devices[2] 48 self.contacts_destination_path = self.log_path + "/" 49 50 def setup_class(self): 51 if not super(BtCarPbapTest, self).setup_class(): 52 return False 53 permissions_list = [ 54 "android.permission.READ_CONTACTS", 55 "android.permission.WRITE_CONTACTS", 56 "android.permission.READ_EXTERNAL_STORAGE" 57 ] 58 for permission in permissions_list: 59 self.pse.adb.shell( 60 "pm grant com.google.android.contacts {}".format(permission)) 61 for permission in permissions_list: 62 self.pce.adb.shell("pm grant com.android.contacts {}".format( 63 permission)) 64 65 # Pair the devices. 66 # This call may block until some specified timeout in bt_test_utils.py. 67 # Grace time inbetween stack state changes 68 69 setup_multiple_devices_for_bt_test(self.android_devices) 70 if not bt_test_utils.pair_pri_to_sec(self.pce, self.pse): 71 self.log.error("Failed to pair.") 72 return False 73 time.sleep(3) 74 if not bt_test_utils.pair_pri_to_sec(self.pce, self.pse2): 75 self.log.error("Failed to pair.") 76 return False 77 78 # Disable the HFP and A2DP profiles. This will ensure only PBAP 79 # gets connected. Also, this will eliminate the auto-connect loop. 80 car_bt_utils.set_car_profile_priorities_off(self.pce, self.pse) 81 car_bt_utils.set_car_profile_priorities_off(self.pce, self.pse2) 82 83 # Enable PBAP on PSE & PCE. 84 85 self.pse.droid.bluetoothChangeProfileAccessPermission( 86 self.pce.droid.bluetoothGetLocalAddress(), 87 BtEnum.BluetoothProfile.PBAP_SERVER.value, 88 BtEnum.BluetoothAccessLevel.ACCESS_ALLOWED.value) 89 90 self.pse2.droid.bluetoothChangeProfileAccessPermission( 91 self.pce.droid.bluetoothGetLocalAddress(), 92 BtEnum.BluetoothProfile.PBAP_SERVER.value, 93 BtEnum.BluetoothAccessLevel.ACCESS_ALLOWED.value) 94 95 bt_test_utils.set_profile_priority( 96 self.pce, self.pse, [BtEnum.BluetoothProfile.PBAP_CLIENT], 97 BtEnum.BluetoothPriorityLevel.PRIORITY_ON) 98 bt_test_utils.set_profile_priority( 99 self.pce, self.pse2, [BtEnum.BluetoothProfile.PBAP_CLIENT], 100 BtEnum.BluetoothPriorityLevel.PRIORITY_ON) 101 102 return True 103 104 def setup_test(self): 105 if not super(BtCarPbapTest, self).setup_test(): 106 return False 107 self.pse.droid.callLogsEraseAll() 108 if not (bt_contacts_utils.erase_contacts(self.pse) and 109 bt_contacts_utils.erase_contacts(self.pce)): 110 return False 111 # Allow all content providers to synchronize. 112 time.sleep(1) 113 return True 114 115 def teardown_test(self): 116 if not super(BtCarPbapTest, self).teardown_test(): 117 return False 118 self.pce.droid.bluetoothPbapClientDisconnect( 119 self.pse.droid.bluetoothGetLocalAddress()) 120 bt_contacts_utils.erase_contacts(self.pse) 121 return True 122 123 def verify_contacts_match(self): 124 bt_contacts_utils.export_device_contacts_to_vcf( 125 self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE) 126 return bt_contacts_utils.count_contacts_with_differences( 127 self.contacts_destination_path, PCE_CONTACTS_FILE, 128 PSE_CONTACTS_FILE) == 0 129 130 def connect_and_verify(self, count): 131 bt_test_utils.connect_pri_to_sec( 132 self.pce, self.pse, 133 set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])) 134 bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 135 count) 136 contacts_added = self.verify_contacts_match() 137 self.pce.droid.bluetoothPbapClientDisconnect( 138 self.pse.droid.bluetoothGetLocalAddress()) 139 contacts_removed = bt_contacts_utils.wait_for_phone_number_update_complete( 140 self.pce, 0) 141 return contacts_added and contacts_removed 142 143 @test_tracker_info(uuid='7dcdecfc-42d1-4f41-b66e-823c8f161356') 144 @BluetoothBaseTest.bt_test_wrap 145 def test_pbap_connect_and_disconnect(self): 146 """Test Connectivity 147 148 Test connecting with the server enabled and disabled 149 150 Precondition: 151 1. Devices are paired. 152 153 Steps: 154 1. Disable permission on PSE to prevent PCE from connecting 155 2. Attempt to connect PCE to PSE 156 3. Verify connection failed 157 4. Enable permission on PSE to allow PCE to connect 158 5. Attempt to connect PCE to PSE 159 6. Verify connection succeeded 160 161 Returns: 162 Pass if True 163 Fail if False 164 """ 165 self.pse.droid.bluetoothChangeProfileAccessPermission( 166 self.pce.droid.bluetoothGetLocalAddress(), 167 BtEnum.BluetoothProfile.PBAP_SERVER.value, 168 BtEnum.BluetoothAccessLevel.ACCESS_DENIED.value) 169 if bt_test_utils.connect_pri_to_sec( 170 self.pce, self.pse, 171 set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])): 172 self.log.error("Client connected and shouldn't be.") 173 return False 174 175 self.pce.droid.bluetoothPbapClientDisconnect( 176 self.pse.droid.bluetoothGetLocalAddress()) 177 178 self.pse.droid.bluetoothChangeProfileAccessPermission( 179 self.pce.droid.bluetoothGetLocalAddress(), 180 BtEnum.BluetoothProfile.PBAP_SERVER.value, 181 BtEnum.BluetoothAccessLevel.ACCESS_ALLOWED.value) 182 183 if not bt_test_utils.connect_pri_to_sec( 184 self.pce, self.pse, 185 set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])): 186 self.log.error("No client connected and should be.") 187 return False 188 189 return True 190 191 @test_tracker_info(uuid='1733efb9-71af-4956-bd3a-0d3167d94d0c') 192 @BluetoothBaseTest.bt_test_wrap 193 def test_contact_download(self): 194 """Test Contact Download 195 196 Test download of contacts from a clean state. 197 198 Precondition: 199 1. Devices are paired. 200 201 Steps: 202 1. Erase contacts from PSE and PCE. 203 2. Add a predefined list of contacts to PSE. 204 3. Connect PCE to PSE to perform transfer. 205 4. Compare transfered contacts. 206 5. Disconnect. 207 6. Verify PCE cleaned up contact list. 208 209 Returns: 210 Pass if True 211 Fail if False 212 """ 213 bt_contacts_utils.generate_contact_list(self.contacts_destination_path, 214 PSE_CONTACTS_FILE, 100) 215 phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf( 216 self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE) 217 bt_test_utils.connect_pri_to_sec( 218 self.pce, self.pse, 219 set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])) 220 bt_contacts_utils.wait_for_phone_number_update_complete( 221 self.pce, phone_numbers_added) 222 if not self.verify_contacts_match(): 223 return False 224 return bt_contacts_utils.erase_contacts(self.pce) 225 226 @test_tracker_info(uuid='99dc6ac6-b7cf-45ce-927b-8c4ebf8ab664') 227 @BluetoothBaseTest.bt_test_wrap 228 def test_modify_phonebook(self): 229 """Test Modify Phonebook 230 231 Test changing contacts and reconnecting PBAP. 232 233 Precondition: 234 1. Devices are paired. 235 236 Steps: 237 1. Add a predefined list of contacts to PSE. 238 2. Connect PCE to PSE to perform transfer. 239 3. Verify that contacts match. 240 4. Change some contacts on the PSE. 241 5. Reconnect PCE to PSE to perform transfer. 242 6. Verify that new contacts match. 243 244 Returns: 245 Pass if True 246 Fail if False 247 """ 248 bt_contacts_utils.generate_contact_list(self.contacts_destination_path, 249 PSE_CONTACTS_FILE, 100) 250 phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf( 251 self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE) 252 if not self.connect_and_verify(phone_numbers_added): 253 return False 254 255 bt_contacts_utils.erase_contacts(self.pse) 256 bt_contacts_utils.generate_contact_list(self.contacts_destination_path, 257 PSE_CONTACTS_FILE, 110, 2) 258 phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf( 259 self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE) 260 return self.connect_and_verify(phone_numbers_added) 261 262 @test_tracker_info(uuid='bbe31bf5-51e8-4175-b266-1c7750e44f5b') 263 @BluetoothBaseTest.bt_test_wrap 264 def test_special_contacts(self): 265 """Test Special Contacts 266 267 Test numerous special cases of contacts that could cause errors. 268 269 Precondition: 270 1. Devices are paired. 271 272 Steps: 273 1. Add a predefined list of contacts to PSE that includes special cases: 274 2. Connect PCE to PSE to perform transfer. 275 3. Verify that contacts match. 276 277 Returns: 278 Pass if True 279 Fail if False 280 """ 281 282 vcards = [] 283 284 # Generate a contact with no email address 285 current_contact = bt_contacts_utils.VCard() 286 current_contact.first_name = "Mr." 287 current_contact.last_name = "Smiley" 288 current_contact.add_phone_number( 289 bt_contacts_utils.generate_random_phone_number()) 290 vcards.append(current_contact) 291 292 # Generate a 2nd contact with the same name but different phone number 293 current_contact = bt_contacts_utils.VCard() 294 current_contact.first_name = "Mr." 295 current_contact.last_name = "Smiley" 296 current_contact.add_phone_number( 297 bt_contacts_utils.generate_random_phone_number()) 298 vcards.append(current_contact) 299 300 # Generate a contact with no name 301 current_contact = bt_contacts_utils.VCard() 302 current_contact.email = "{}@gmail.com".format( 303 bt_contacts_utils.generate_random_string()) 304 current_contact.add_phone_number( 305 bt_contacts_utils.generate_random_phone_number()) 306 vcards.append(current_contact) 307 308 # Generate a contact with random characters in its name 309 current_contact = bt_contacts_utils.VCard() 310 current_contact.first_name = bt_contacts_utils.generate_random_string() 311 current_contact.last_name = bt_contacts_utils.generate_random_string() 312 current_contact.add_phone_number( 313 bt_contacts_utils.generate_random_phone_number()) 314 vcards.append(current_contact) 315 316 # Generate a contact with only a phone number 317 current_contact = bt_contacts_utils.VCard() 318 current_contact.add_phone_number( 319 bt_contacts_utils.generate_random_phone_number()) 320 vcards.append(current_contact) 321 322 # Generate a 2nd contact with only a phone number 323 current_contact = bt_contacts_utils.VCard() 324 current_contact.add_phone_number( 325 bt_contacts_utils.generate_random_phone_number()) 326 vcards.append(current_contact) 327 328 bt_contacts_utils.create_new_contacts_vcf_from_vcards( 329 self.contacts_destination_path, PSE_CONTACTS_FILE, vcards) 330 331 phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf( 332 self.pse, self.contacts_destination_path, PSE_CONTACTS_FILE) 333 334 return self.connect_and_verify(phone_numbers_added) 335 336 @test_tracker_info(uuid='2aa2bd00-86cc-4f39-a06a-90b17ea5b320') 337 @BluetoothBaseTest.bt_test_wrap 338 def test_call_log(self): 339 """Test Call Log 340 341 Test that Call Logs are transfered 342 343 Precondition: 344 1. Devices are paired. 345 346 Steps: 347 1. Add a predefined list of calls to the PSE call log. 348 2. Connect PCE to PSE to allow call log transfer 349 3. Verify the Missed, Incoming, and Outgoing Call History 350 351 Returns: 352 Pass if True 353 Fail if False 354 """ 355 356 bt_contacts_utils.add_call_log( 357 self.pse, bt_contacts_utils.INCOMMING_CALL_TYPE, 358 bt_contacts_utils.generate_random_phone_number().phone_number, 359 int(time.time() * 1000)) 360 bt_contacts_utils.add_call_log( 361 self.pse, bt_contacts_utils.INCOMMING_CALL_TYPE, 362 bt_contacts_utils.generate_random_phone_number().phone_number, 363 int(time.time()) * 1000 - 4 * CALL_LOG_TIME_OFFSET_IN_MSEC) 364 bt_contacts_utils.add_call_log( 365 self.pse, bt_contacts_utils.OUTGOING_CALL_TYPE, 366 bt_contacts_utils.generate_random_phone_number().phone_number, 367 int(time.time()) * 1000 - CALL_LOG_TIME_OFFSET_IN_MSEC) 368 bt_contacts_utils.add_call_log( 369 self.pse, bt_contacts_utils.MISSED_CALL_TYPE, 370 bt_contacts_utils.generate_random_phone_number().phone_number, 371 int(time.time()) * 1000 - 2 * CALL_LOG_TIME_OFFSET_IN_MSEC) 372 bt_contacts_utils.add_call_log( 373 self.pse, bt_contacts_utils.MISSED_CALL_TYPE, 374 bt_contacts_utils.generate_random_phone_number().phone_number, 375 int(time.time()) * 1000 - 2 * CALL_LOG_TIME_OFFSET_IN_MSEC) 376 377 self.pce.droid.bluetoothPbapClientDisconnect( 378 self.pse.droid.bluetoothGetLocalAddress()) 379 self.pce.droid.bluetoothPbapClientDisconnect( 380 self.pse2.droid.bluetoothGetLocalAddress()) 381 382 bt_test_utils.connect_pri_to_sec( 383 self.pce, self.pse, 384 set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])) 385 pse_call_log_count = self.pse.droid.callLogGetCount() 386 self.log.info("Waiting for {} call logs to be transfered".format( 387 pse_call_log_count)) 388 bt_contacts_utils.wait_for_call_log_update_complete(self.pce, 389 pse_call_log_count) 390 391 if not bt_contacts_utils.get_and_compare_call_logs( 392 self.pse, self.pce, bt_contacts_utils.INCOMMING_CALL_TYPE): 393 return False 394 if not bt_contacts_utils.get_and_compare_call_logs( 395 self.pse, self.pce, bt_contacts_utils.OUTGOING_CALL_TYPE): 396 return False 397 if not bt_contacts_utils.get_and_compare_call_logs( 398 self.pse, self.pce, bt_contacts_utils.MISSED_CALL_TYPE): 399 return False 400 401 return True 402 403 @test_tracker_info(uuid='bb018bf4-5a61-478d-acce-eef88050e489') 404 @BluetoothBaseTest.bt_test_wrap 405 def test_multiple_phones(self): 406 """Test Multiple Phones 407 408 Test that connects two phones and confirms contacts are transfered 409 and merged while still being associated with their original phone. 410 411 Precondition: 412 1. Devices are paired. 413 414 Steps: 415 1. Add a unique list of contacts to PSE on each phone. 416 2. Connect PCE to PSE 1 to perform transfer. 417 3. Verify contacts match. 418 4. Connect PCE to PSE 2 to perform transfer. 419 5. Verify that the PCE has a union set of contacts from 420 PSE 1 and PSE 2. 421 6. Disconnect PCE from PSE 1 to clean up contacts. 422 7. Verify that only PSE 2 contacts remain on PCE and they match. 423 8. Disconnect PCE from PSE 2 to clean up contacts. 424 425 Returns: 426 Pass if True 427 Fail if False 428 """ 429 430 PSE1_CONTACTS_FILE = "{}{}".format(PSE_CONTACTS_FILE, "1") 431 PSE2_CONTACTS_FILE = "{}{}".format(PSE_CONTACTS_FILE, "2") 432 433 bt_contacts_utils.generate_contact_list(self.contacts_destination_path, 434 PSE1_CONTACTS_FILE, 100) 435 phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf( 436 self.pse, self.contacts_destination_path, PSE1_CONTACTS_FILE) 437 bt_contacts_utils.generate_contact_list(self.contacts_destination_path, 438 PSE2_CONTACTS_FILE, 100) 439 phone_numbers_added = bt_contacts_utils.import_device_contacts_from_vcf( 440 self.pse2, self.contacts_destination_path, PSE2_CONTACTS_FILE) 441 442 self.pce.droid.bluetoothPbapClientDisconnect( 443 self.pse.droid.bluetoothGetLocalAddress()) 444 self.pce.droid.bluetoothPbapClientDisconnect( 445 self.pse2.droid.bluetoothGetLocalAddress()) 446 447 bt_test_utils.connect_pri_to_sec( 448 self.pce, self.pse, 449 set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])) 450 bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 100) 451 bt_contacts_utils.export_device_contacts_to_vcf( 452 self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE) 453 pse1_matches = bt_contacts_utils.count_contacts_with_differences( 454 self.contacts_destination_path, PCE_CONTACTS_FILE, 455 PSE1_CONTACTS_FILE) == 0 456 457 bt_test_utils.connect_pri_to_sec( 458 self.pce, self.pse2, 459 set([BtEnum.BluetoothProfile.PBAP_CLIENT.value])) 460 461 bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 200) 462 463 bt_contacts_utils.export_device_contacts_to_vcf( 464 self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE) 465 466 merged_file = open('{}{}'.format(self.contacts_destination_path, 467 MERGED_CONTACTS_FILE), 'w') 468 for contacts_file in [PSE1_CONTACTS_FILE, PSE2_CONTACTS_FILE]: 469 infile = open(self.contacts_destination_path + contacts_file) 470 merged_file.write(infile.read()) 471 472 self.log.info("Checking combined phonebook.") 473 pse1andpse2_matches = bt_contacts_utils.count_contacts_with_differences( 474 self.contacts_destination_path, PCE_CONTACTS_FILE, 475 MERGED_CONTACTS_FILE) == 0 476 477 self.pce.droid.bluetoothPbapClientDisconnect( 478 self.pse.droid.bluetoothGetLocalAddress()) 479 bt_contacts_utils.wait_for_phone_number_update_complete(self.pce, 100) 480 481 self.log.info("Checking phonebook after disconnecting first device.") 482 bt_contacts_utils.export_device_contacts_to_vcf( 483 self.pce, self.contacts_destination_path, PCE_CONTACTS_FILE) 484 pse2_matches = bt_contacts_utils.count_contacts_with_differences( 485 self.contacts_destination_path, PCE_CONTACTS_FILE, 486 PSE2_CONTACTS_FILE) == 0 487 488 bt_contacts_utils.erase_contacts(self.pse) 489 bt_contacts_utils.erase_contacts(self.pse2) 490 return pse1_matches and pse2_matches and pse1andpse2_matches 491