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