1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.contacts; 18 19 import static android.content.pm.Flags.FLAG_STAY_STOPPED; 20 21 import android.accounts.Account; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.content.pm.ProviderInfo; 29 import android.database.Cursor; 30 import android.database.MatrixCursor; 31 import android.net.Uri; 32 import android.os.Bundle; 33 import android.platform.test.annotations.RequiresFlagsEnabled; 34 import android.platform.test.flag.junit.CheckFlagsRule; 35 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 36 import android.provider.ContactsContract; 37 import android.provider.ContactsContract.AggregationExceptions; 38 import android.provider.ContactsContract.Contacts; 39 import android.provider.ContactsContract.Directory; 40 import android.provider.ContactsContract.RawContacts; 41 import android.test.mock.MockContentProvider; 42 import android.util.Log; 43 44 import androidx.test.platform.app.InstrumentationRegistry; 45 import androidx.test.runner.AndroidJUnit4; 46 47 import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns; 48 49 import com.google.android.collect.Lists; 50 51 import org.junit.Before; 52 import org.junit.Rule; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 56 import java.util.Arrays; 57 import java.util.Set; 58 59 /** 60 * Unit tests for {@link ContactDirectoryManager}. Run the test like this: 61 * 62 adb shell am instrument -e class com.android.providers.contacts.ContactDirectoryManagerTest \ 63 -w com.android.providers.contacts.tests/android.test.InstrumentationTestRunner 64 * 65 */ 66 @RunWith(AndroidJUnit4.class) 67 public class ContactDirectoryManagerTest extends BaseContactsProvider2Test { 68 69 @Rule 70 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 71 private static final String TAG = "ContactDirectoryManagerTest"; 72 73 private ContactsMockPackageManager mPackageManager; 74 75 private ContactsProvider2 mProvider; 76 77 private ContactDirectoryManager mDirectoryManager; 78 79 public static class MockContactDirectoryProvider extends MockContentProvider { 80 81 private String mAuthority; 82 83 private MatrixCursor mResponse; 84 85 @Override attachInfoForTesting(Context context, ProviderInfo info)86 public void attachInfoForTesting(Context context, ProviderInfo info) { 87 mAuthority = info.authority; 88 } 89 createResponseCursor()90 public MatrixCursor createResponseCursor() { 91 mResponse = new MatrixCursor( 92 new String[] { Directory.ACCOUNT_NAME, Directory.ACCOUNT_TYPE, 93 Directory.DISPLAY_NAME, Directory.TYPE_RESOURCE_ID, 94 Directory.EXPORT_SUPPORT, Directory.SHORTCUT_SUPPORT, 95 Directory.PHOTO_SUPPORT }); 96 97 return mResponse; 98 } 99 100 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)101 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 102 String sortOrder) { 103 104 if (uri.toString().equals("content://" + mAuthority + "/directories")) { 105 // Should tolerate multiple queries. 106 mResponse.moveToPosition(-1); 107 return mResponse; 108 } else if (uri.toString().startsWith("content://" + mAuthority + "/contacts")) { 109 MatrixCursor cursor = new MatrixCursor( 110 new String[] { "projection", "selection", "selectionArgs", "sortOrder", 111 "accountName", "accountType"}); 112 cursor.addRow(new Object[] { 113 Lists.newArrayList(projection).toString(), 114 selection, 115 Lists.newArrayList(selectionArgs).toString(), 116 sortOrder, 117 uri.getQueryParameter(RawContacts.ACCOUNT_NAME), 118 uri.getQueryParameter(RawContacts.ACCOUNT_TYPE), 119 }); 120 return cursor; 121 } else if (uri.toString().startsWith( 122 "content://" + mAuthority + "/aggregation_exceptions")) { 123 return new MatrixCursor(projection); 124 } 125 126 fail("Unexpected uri: " + uri); 127 return null; 128 } 129 } 130 131 @Before 132 @Override setUp()133 public void setUp() throws Exception { 134 135 super.setUp(); 136 137 mProvider = (ContactsProvider2) getProvider(); 138 mDirectoryManager = mProvider.getContactDirectoryManagerForTest(); 139 140 mPackageManager = (ContactsMockPackageManager) getProvider() 141 .getContext().getPackageManager(); 142 } 143 144 @Override getContextPackageName()145 protected String getContextPackageName() { 146 // In this test, we need to use the real package name, because that'll be recorded in the 147 // directory table, and if it's wrong, the tests would get confused. 148 return mContext.getPackageName(); 149 } 150 151 @Test testIsDirectoryProvider()152 public void testIsDirectoryProvider() { 153 ProviderInfo provider = new ProviderInfo(); 154 155 // Null -- just return false. 156 assertFalse(ContactDirectoryManager.isDirectoryProvider(null)); 157 158 // No metadata 159 assertFalse(ContactDirectoryManager.isDirectoryProvider(provider)); 160 161 // No CONTACT_DIRECTORY_META_DATA 162 provider.metaData = new Bundle(); 163 assertFalse(ContactDirectoryManager.isDirectoryProvider(provider)); 164 165 // CONTACT_DIRECTORY_META_DATA is a string 166 provider.metaData.putString("android.content.ContactDirectory", ""); 167 assertFalse(ContactDirectoryManager.isDirectoryProvider(provider)); 168 169 // CONTACT_DIRECTORY_META_DATA is false 170 provider.metaData.putBoolean("android.content.ContactDirectory", false); 171 assertFalse(ContactDirectoryManager.isDirectoryProvider(provider)); 172 173 // CONTACT_DIRECTORY_META_DATA is true 174 provider.metaData.putBoolean("android.content.ContactDirectory", true); 175 assertTrue(ContactDirectoryManager.isDirectoryProvider(provider)); 176 } 177 178 @Test testScanAllProviders()179 public void testScanAllProviders() throws Exception { 180 mPackageManager.setInstalledPackages( 181 Lists.newArrayList( 182 createProviderPackage("test.package1", "authority1"), 183 createProviderPackage("test.package2", "authority2"), 184 createPackage("test.packageX", "authorityX", false))); 185 186 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 187 MockContactDirectoryProvider.class, "authority1"); 188 189 MatrixCursor response1 = provider1.createResponseCursor(); 190 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 191 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 192 Directory.PHOTO_SUPPORT_FULL_SIZE_ONLY); 193 addDirectoryRow(response1, "account-name2", "account-type2", "display-name2", 2, 194 Directory.EXPORT_SUPPORT_ANY_ACCOUNT, Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY, 195 Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY); 196 197 MockContactDirectoryProvider provider2 = (MockContactDirectoryProvider) addProvider( 198 MockContactDirectoryProvider.class, "authority2"); 199 200 MatrixCursor response2 = provider2.createResponseCursor(); 201 addDirectoryRow(response2, "account-name3", "account-type3", "display-name3", 3, 202 Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, Directory.SHORTCUT_SUPPORT_FULL, 203 Directory.PHOTO_SUPPORT_FULL); 204 205 assertEquals(3, mDirectoryManager.scanAllPackages(/* rescan=*/ false)); 206 207 Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, 208 /* order by=*/ Directory.DIRECTORY_AUTHORITY + "," + Directory.ACCOUNT_NAME + 209 "," + Directory._ID ); 210 211 TestUtils.dumpCursor(cursor); 212 assertEquals(5, cursor.getCount()); 213 214 assertTrue(cursor.moveToPosition(0)); 215 assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1", 216 "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 217 Directory.PHOTO_SUPPORT_FULL_SIZE_ONLY); 218 219 assertTrue(cursor.moveToPosition(1)); 220 assertDirectoryRow(cursor, "test.package1", "authority1", "account-name2", "account-type2", 221 "display-name2", 2, Directory.EXPORT_SUPPORT_ANY_ACCOUNT, 222 Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY, Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY); 223 224 assertTrue(cursor.moveToPosition(2)); 225 assertDirectoryRow(cursor, "test.package2", "authority2", "account-name3", "account-type3", 226 "display-name3", 3, Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, 227 Directory.SHORTCUT_SUPPORT_FULL, Directory.PHOTO_SUPPORT_FULL); 228 229 assertTrue(cursor.moveToPosition(3)); 230 assertDirectoryRow(cursor, mContext.getPackageName(), 231 "com.android.contacts", null, null, 232 null, -1 /* =any */, Directory.EXPORT_SUPPORT_NONE, 233 Directory.SHORTCUT_SUPPORT_FULL, Directory.PHOTO_SUPPORT_FULL); 234 235 assertTrue(cursor.moveToPosition(4)); 236 assertDirectoryRow(cursor, mContext.getPackageName(), 237 "com.android.contacts", null, null, 238 null, -1 /* =any */, Directory.EXPORT_SUPPORT_NONE, 239 Directory.SHORTCUT_SUPPORT_FULL, Directory.PHOTO_SUPPORT_FULL); 240 241 cursor.close(); 242 } 243 244 @Test testScanAllProviders_scanCondition()245 public void testScanAllProviders_scanCondition() throws Exception { 246 testScanAllProviders(); 247 248 // Nothing has changed, so no scanning. 249 assertEquals(0, mDirectoryManager.scanAllPackages(/* rescan=*/ false)); 250 251 // rescan = true, so a full-scan should happen. 252 assertEquals(3, mDirectoryManager.scanAllPackages(/* rescan=*/ true)); 253 254 // Change GAL packages, a scan should happen. 255 mPackageManager.setInstalledPackages( 256 Lists.newArrayList( 257 createProviderPackage("test.package2", "authority2"), 258 createPackage("test.packageX", "authorityX", false))); 259 assertEquals(1, mDirectoryManager.scanAllPackages(/* rescan=*/ false)); 260 261 // Remove the non-GAL package, no scan should happen. 262 mPackageManager.setInstalledPackages( 263 Lists.newArrayList( 264 createProviderPackage("test.package2", "authority2"))); 265 assertEquals(0, mDirectoryManager.scanAllPackages(/* rescan=*/ false)); 266 267 // Remove GAL package 2 and add 1, a scan should happen. 268 mPackageManager.setInstalledPackages( 269 Lists.newArrayList( 270 createProviderPackage("test.package1", "authority1"), 271 createPackage("test.packageX", "authorityX", false))); 272 assertEquals(2, mDirectoryManager.scanAllPackages(/* rescan=*/ false)); 273 274 } 275 276 @Test testPackageInstalled()277 public void testPackageInstalled() throws Exception { 278 mPackageManager.setInstalledPackages( 279 Lists.newArrayList(createProviderPackage("test.package1", "authority1"), 280 createPackage("test.packageX", "authorityX", false))); 281 282 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 283 MockContactDirectoryProvider.class, "authority1"); 284 285 MatrixCursor response1 = provider1.createResponseCursor(); 286 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 287 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 288 Directory.PHOTO_SUPPORT_FULL); 289 290 mDirectoryManager.scanAllPackages(/* rescan=*/ false); 291 292 // At this point the manager has discovered a single directory (plus two 293 // standard ones). 294 Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null); 295 assertEquals(3, cursor.getCount()); 296 cursor.close(); 297 298 // Pretend to install another package 299 MockContactDirectoryProvider provider2 = (MockContactDirectoryProvider) addProvider( 300 MockContactDirectoryProvider.class, "authority2"); 301 302 MatrixCursor response2 = provider2.createResponseCursor(); 303 addDirectoryRow(response2, "account-name3", "account-type3", "display-name3", 3, 304 Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, Directory.SHORTCUT_SUPPORT_FULL, 305 Directory.PHOTO_SUPPORT_FULL); 306 307 mPackageManager.getInstalledPackages(0).add( 308 createProviderPackage("test.package2", "authority2")); 309 310 mProvider.onPackageChanged("test.package2"); 311 312 cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null); 313 assertEquals(4, cursor.getCount()); 314 315 cursor.moveToPosition(2); 316 assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1", 317 "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 318 Directory.PHOTO_SUPPORT_FULL); 319 320 cursor.moveToNext(); 321 assertDirectoryRow(cursor, "test.package2", "authority2", "account-name3", "account-type3", 322 "display-name3", 3, Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, 323 Directory.SHORTCUT_SUPPORT_FULL, Directory.PHOTO_SUPPORT_FULL); 324 325 cursor.close(); 326 } 327 328 @Test testPackageUninstalled()329 public void testPackageUninstalled() throws Exception { 330 mPackageManager.setInstalledPackages( 331 Lists.newArrayList( 332 createProviderPackage("test.package1", "authority1"), 333 createProviderPackage("test.package2", "authority2"), 334 createPackage("test.packageX", "authorityX", false))); 335 336 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 337 MockContactDirectoryProvider.class, "authority1"); 338 339 MatrixCursor response1 = provider1.createResponseCursor(); 340 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 341 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 342 Directory.PHOTO_SUPPORT_NONE); 343 344 MockContactDirectoryProvider provider2 = (MockContactDirectoryProvider) addProvider( 345 MockContactDirectoryProvider.class, "authority2"); 346 347 MatrixCursor response2 = provider2.createResponseCursor(); 348 addDirectoryRow(response2, "account-name3", "account-type3", "display-name3", 3, 349 Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, Directory.SHORTCUT_SUPPORT_FULL, 350 Directory.PHOTO_SUPPORT_FULL); 351 352 mDirectoryManager.scanAllPackages(/* rescan=*/ false); 353 354 // At this point the manager has discovered two custom directories. 355 Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null); 356 assertEquals(4, cursor.getCount()); 357 cursor.close(); 358 359 // Pretend to uninstall one of the packages 360 mPackageManager.getInstalledPackages(0).remove(1); 361 362 mProvider.onPackageChanged("test.package2"); 363 364 cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null); 365 assertEquals(3, cursor.getCount()); 366 367 cursor.moveToPosition(2); 368 assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1", 369 "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 370 Directory.PHOTO_SUPPORT_NONE); 371 372 cursor.close(); 373 } 374 375 @Test testPackageReplaced()376 public void testPackageReplaced() throws Exception { 377 mPackageManager.setInstalledPackages( 378 Lists.newArrayList( 379 createProviderPackage("test.package1", "authority1"), 380 createProviderPackage("test.package2", "authority2"), 381 createPackage("test.packageX", "authorityX", false))); 382 383 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 384 MockContactDirectoryProvider.class, "authority1"); 385 386 MatrixCursor response1 = provider1.createResponseCursor(); 387 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 388 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 389 Directory.PHOTO_SUPPORT_NONE); 390 391 MockContactDirectoryProvider provider2 = (MockContactDirectoryProvider) addProvider( 392 MockContactDirectoryProvider.class, "authority2"); 393 394 MatrixCursor response2 = provider2.createResponseCursor(); 395 addDirectoryRow(response2, "account-name3", "account-type3", "display-name3", 3, 396 Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, Directory.SHORTCUT_SUPPORT_FULL, 397 Directory.PHOTO_SUPPORT_FULL); 398 399 mDirectoryManager.scanAllPackages(/* rescan=*/ false); 400 401 // At this point the manager has discovered two custom directories. 402 Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null); 403 assertEquals(4, cursor.getCount()); 404 cursor.close(); 405 406 // Pretend to replace the package with a different provider inside 407 MatrixCursor response3 = provider2.createResponseCursor(); 408 addDirectoryRow(response3, "account-name4", "account-type4", "display-name4", 4, 409 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 410 Directory.PHOTO_SUPPORT_NONE); 411 412 mProvider.onPackageChanged("test.package2"); 413 414 cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null); 415 assertEquals(4, cursor.getCount()); 416 417 cursor.moveToPosition(2); 418 assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1", 419 "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 420 Directory.PHOTO_SUPPORT_NONE); 421 422 cursor.moveToNext(); 423 assertDirectoryRow(cursor, "test.package2", "authority2", "account-name4", "account-type4", 424 "display-name4", 4, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 425 Directory.PHOTO_SUPPORT_NONE); 426 427 cursor.close(); 428 } 429 430 /** 431 * Tests if the manager works correctly when the package name for a directory is changed 432 * (on system update). 433 */ 434 @Test testPackageRenamed()435 public void testPackageRenamed() throws Exception { 436 mPackageManager.setInstalledPackages( 437 Lists.newArrayList( 438 createProviderPackage("test.package1", "authority1"), 439 createProviderPackage("test.package2", "authority2"), 440 createPackage("test.packageX", "authorityX", false))); 441 442 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 443 MockContactDirectoryProvider.class, "authority1"); 444 445 MatrixCursor response1 = provider1.createResponseCursor(); 446 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 447 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 448 Directory.PHOTO_SUPPORT_NONE); 449 450 MockContactDirectoryProvider provider2 = (MockContactDirectoryProvider) addProvider( 451 MockContactDirectoryProvider.class, "authority2"); 452 453 MatrixCursor response2 = provider2.createResponseCursor(); 454 addDirectoryRow(response2, "account-name2", "account-type2", "display-name2", 2, 455 Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, Directory.SHORTCUT_SUPPORT_FULL, 456 Directory.PHOTO_SUPPORT_FULL); 457 458 mDirectoryManager.scanAllPackages(/* rescan=*/ false); 459 460 // At this point the manager has discovered two custom directories. 461 Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null); 462 assertEquals(4, cursor.getCount()); 463 cursor.close(); 464 465 // Pretend to rename the package name of a package on system update. 466 PackageInfo info = mPackageManager.getInstalledPackages(0).get(1); 467 info.packageName = "test.package3"; 468 info.providers[0].packageName = "test.package3"; 469 MatrixCursor response3 = provider2.createResponseCursor(); 470 // Change resource id. 471 addDirectoryRow(response3, "account-name2", "account-type2", "display-name2", 3, 472 Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, Directory.SHORTCUT_SUPPORT_FULL, 473 Directory.PHOTO_SUPPORT_FULL); 474 475 // When this happens on reboot, scanAllPackages() is called instead of onPackageChanged() 476 // (as part of ContactsProvider2 initialization). 477 // Accounts won't be affected so false should be passed here. 478 mDirectoryManager.scanAllPackages(false); 479 480 cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null); 481 // We should have columns for default, local_invisible, test.package1, and test.package3. 482 // The row for test.package2 should not remain. 483 assertEquals(4, cursor.getCount()); 484 485 cursor.moveToPosition(2); 486 assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1", 487 "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 488 Directory.PHOTO_SUPPORT_NONE); 489 490 cursor.moveToNext(); 491 assertDirectoryRow(cursor, "test.package3", "authority2", "account-name2", "account-type2", 492 "display-name2", 3, Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, 493 Directory.SHORTCUT_SUPPORT_FULL, Directory.PHOTO_SUPPORT_FULL); 494 495 cursor.close(); 496 } 497 498 @Test testAccountRemoval()499 public void testAccountRemoval() throws Exception { 500 mPackageManager.setInstalledPackages( 501 Lists.newArrayList( 502 createProviderPackage("test.package1", "authority1"), 503 createProviderPackage("test.package2", "authority2"), 504 createPackage("test.packageX", "authorityX", false))); 505 506 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 507 MockContactDirectoryProvider.class, "authority1"); 508 509 Account[] accounts = new Account[]{ 510 new Account("account-name1", "account-type1"), 511 new Account("account-name2", "account-type2")}; 512 mActor.setAccounts(accounts); 513 ((ContactsProvider2)getProvider()).onAccountsUpdated(accounts); 514 515 MatrixCursor response1 = provider1.createResponseCursor(); 516 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 517 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 518 Directory.PHOTO_SUPPORT_NONE); 519 addDirectoryRow(response1, "account-name2", "account-type2", "display-name2", 2, 520 Directory.EXPORT_SUPPORT_ANY_ACCOUNT, Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY, 521 Directory.PHOTO_SUPPORT_FULL_SIZE_ONLY); 522 523 mDirectoryManager.scanAllPackages(/* rescan=*/ false); 524 525 accounts = new Account[]{new Account("account-name1", "account-type1")}; 526 mActor.setAccounts(accounts); 527 ((ContactsProvider2)getProvider()).onAccountsUpdated(accounts); 528 529 Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null); 530 assertEquals(3, cursor.getCount()); 531 532 cursor.moveToPosition(2); 533 assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1", 534 "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 535 Directory.PHOTO_SUPPORT_NONE); 536 537 cursor.close(); 538 } 539 540 @Test testNotifyDirectoryChange()541 public void testNotifyDirectoryChange() throws Exception { 542 mPackageManager.setInstalledPackages( 543 Lists.newArrayList(createProviderPackage("test.package1", "authority1"), 544 createPackage("test.packageX", "authorityX", false))); 545 546 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 547 MockContactDirectoryProvider.class, "authority1"); 548 549 MatrixCursor response1 = provider1.createResponseCursor(); 550 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 551 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 552 Directory.PHOTO_SUPPORT_NONE); 553 554 mDirectoryManager.scanAllPackages(/* rescan=*/ false); 555 556 // Pretend to replace the package with a different provider inside 557 MatrixCursor response2 = provider1.createResponseCursor(); 558 addDirectoryRow(response2, "account-name2", "account-type2", "display-name2", 2, 559 Directory.EXPORT_SUPPORT_ANY_ACCOUNT, Directory.SHORTCUT_SUPPORT_FULL, 560 Directory.PHOTO_SUPPORT_FULL); 561 562 ContactsContract.Directory.notifyDirectoryChange(mResolver); 563 564 Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null); 565 assertEquals(3, cursor.getCount()); 566 567 cursor.moveToPosition(2); 568 assertDirectoryRow(cursor, "test.package1", "authority1", "account-name2", "account-type2", 569 "display-name2", 2, Directory.EXPORT_SUPPORT_ANY_ACCOUNT, 570 Directory.SHORTCUT_SUPPORT_FULL, Directory.PHOTO_SUPPORT_FULL); 571 572 cursor.close(); 573 } 574 575 @Test testForwardingToDirectoryProvider()576 public void testForwardingToDirectoryProvider() throws Exception { 577 mPackageManager.setInstalledPackages( 578 Lists.newArrayList(createProviderPackage("test.package1", "authority1"), 579 createPackage("test.packageX", "authorityX", false))); 580 581 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 582 MockContactDirectoryProvider.class, "authority1"); 583 584 MatrixCursor response1 = provider1.createResponseCursor(); 585 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 586 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 587 Directory.PHOTO_SUPPORT_NONE); 588 589 mDirectoryManager.scanAllPackages(/* rescan=*/ false); 590 591 Cursor cursor = mResolver.query( 592 Directory.CONTENT_URI, new String[] { Directory._ID }, null, null, null); 593 cursor.moveToPosition(2); 594 long directoryId = cursor.getLong(0); 595 cursor.close(); 596 597 Uri contentUri = Contacts.CONTENT_URI.buildUpon().appendQueryParameter( 598 ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build(); 599 600 // The request should be forwarded to TestProvider, which will simply 601 // package arguments and return them to us for verification 602 cursor = mResolver.query(contentUri, 603 new String[]{"f1", "f2"}, "query", new String[]{"s1", "s2"}, "so"); 604 assertNotNull(cursor); 605 assertEquals(1, cursor.getCount()); 606 cursor.moveToFirst(); 607 assertEquals("[f1, f2]", cursor.getString(cursor.getColumnIndex("projection"))); 608 assertEquals("query", cursor.getString(cursor.getColumnIndex("selection"))); 609 assertEquals("[s1, s2]", cursor.getString(cursor.getColumnIndex("selectionArgs"))); 610 assertEquals("so", cursor.getString(cursor.getColumnIndex("sortOrder"))); 611 assertEquals("account-name1", cursor.getString(cursor.getColumnIndex("accountName"))); 612 assertEquals("account-type1", cursor.getString(cursor.getColumnIndex("accountType"))); 613 cursor.close(); 614 } 615 616 @Test testProjectionPopulated()617 public void testProjectionPopulated() throws Exception { 618 mPackageManager.setInstalledPackages( 619 Lists.newArrayList(createProviderPackage("test.package1", "authority1"), 620 createPackage("test.packageX", "authorityX", false))); 621 622 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 623 MockContactDirectoryProvider.class, "authority1"); 624 625 MatrixCursor response1 = provider1.createResponseCursor(); 626 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 627 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 628 Directory.PHOTO_SUPPORT_NONE); 629 630 mDirectoryManager.scanAllPackages(/* rescan=*/ false); 631 632 Cursor cursor = mResolver.query( 633 Directory.CONTENT_URI, new String[] { Directory._ID }, null, null, null); 634 cursor.moveToPosition(2); 635 long directoryId = cursor.getLong(0); 636 cursor.close(); 637 638 Uri contentUri = AggregationExceptions.CONTENT_URI.buildUpon().appendQueryParameter( 639 ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build(); 640 641 // The request should be forwarded to TestProvider, which will return an empty cursor 642 // but the projection should be correctly populated by ContactProvider 643 assertProjection(contentUri, new String[]{ 644 AggregationExceptionColumns._ID, 645 AggregationExceptions.TYPE, 646 AggregationExceptions.RAW_CONTACT_ID1, 647 AggregationExceptions.RAW_CONTACT_ID2, 648 }); 649 } 650 651 /** 652 * Test {@link ContactDirectoryManager#getDirectoryProviderPackages} with the actual 653 * package manager, and see if it returns the google sync package. 654 */ 655 @Test testGetDirectoryProviderPackages()656 public void testGetDirectoryProviderPackages() { 657 final PackageManager pm = mContext.getPackageManager(); 658 final String googleSync = "com.google.android.gms"; 659 660 // Skip if the package is not installed. 661 try { 662 pm.getPackageInfo(googleSync, 0); 663 } catch (NameNotFoundException e) { 664 Log.w(TAG, googleSync + " not installed. Skipping..."); 665 return; 666 } 667 668 try { 669 InstrumentationRegistry.getInstrumentation().getUiAutomation() 670 .executeShellCommand("am wait-for-broadcast-idle"); 671 Thread.sleep(1000); // wait for the system 672 } catch (Exception ignored) { } 673 674 // If installed, getDirectoryProviderPackages() should return it. 675 Set<String> dirProviderPackages = ContactDirectoryManager.getDirectoryProviderPackages(pm); 676 assertTrue(googleSync + " package not found in the list of directory provider packages: " 677 + Arrays.toString(dirProviderPackages.toArray()), 678 dirProviderPackages.contains(googleSync)); 679 } 680 681 @Test testUpdateDirectoriesForUnstoppedPackage()682 public void testUpdateDirectoriesForUnstoppedPackage() throws Exception { 683 mPackageManager.setInstalledPackages( 684 Lists.newArrayList(createProviderPackage("test.package1", "authority1"))); 685 686 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 687 MockContactDirectoryProvider.class, "authority1"); 688 MatrixCursor response1 = provider1.createResponseCursor(); 689 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 690 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 691 Directory.PHOTO_SUPPORT_NONE); 692 693 PackageInfo packageInfo = mPackageManager.getPackageInfo("test.package1", 694 PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA); 695 696 ApplicationInfo applicationInfo = new ApplicationInfo(); 697 applicationInfo.packageName = packageInfo.packageName; 698 packageInfo.applicationInfo = applicationInfo; 699 // Package is not in stopped state 700 packageInfo.applicationInfo.flags = 0; 701 702 assertEquals(mDirectoryManager.updateDirectoriesForPackage(packageInfo, true).size(), 1); 703 } 704 705 @Test 706 @RequiresFlagsEnabled(FLAG_STAY_STOPPED) testUpdateDirectoriesForStoppedPackage()707 public void testUpdateDirectoriesForStoppedPackage() throws Exception { 708 mPackageManager.setInstalledPackages( 709 Lists.newArrayList(createProviderPackage("test.package1", "authority1"))); 710 711 MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider( 712 MockContactDirectoryProvider.class, "authority1"); 713 MatrixCursor response1 = provider1.createResponseCursor(); 714 addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1, 715 Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE, 716 Directory.PHOTO_SUPPORT_NONE); 717 718 PackageInfo packageInfo = mPackageManager.getPackageInfo("test.package1", 719 PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA); 720 721 ApplicationInfo applicationInfo = new ApplicationInfo(); 722 applicationInfo.packageName = packageInfo.packageName; 723 packageInfo.applicationInfo = applicationInfo; 724 // Put the package in stopped state: set associated app flags to true 725 packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED; 726 727 assertNull(mDirectoryManager.updateDirectoriesForPackage(packageInfo, true)); 728 } 729 createProviderPackage(String packageName, String authority)730 protected PackageInfo createProviderPackage(String packageName, String authority) { 731 return createPackage(packageName, authority, true); 732 } 733 createPackage(String packageName, String authority, boolean isDirectoryProvider)734 protected PackageInfo createPackage(String packageName, String authority, 735 boolean isDirectoryProvider) { 736 PackageInfo providerPackage = new PackageInfo(); 737 providerPackage.packageName = packageName; 738 ProviderInfo providerInfo = new ProviderInfo(); 739 providerInfo.packageName = providerPackage.packageName; 740 providerInfo.authority = authority; 741 if (isDirectoryProvider) { 742 providerInfo.metaData = new Bundle(); 743 providerInfo.metaData.putBoolean("android.content.ContactDirectory", true); 744 } 745 providerPackage.providers = new ProviderInfo[] { providerInfo }; 746 return providerPackage; 747 } 748 addDirectoryRow(MatrixCursor cursor, String accountName, String accountType, String displayName, int typeResourceId, int exportSupport, int shortcutSupport, int photoSupport)749 protected void addDirectoryRow(MatrixCursor cursor, String accountName, String accountType, 750 String displayName, int typeResourceId, int exportSupport, int shortcutSupport, 751 int photoSupport) { 752 Object[] row = new Object[cursor.getColumnCount()]; 753 row[cursor.getColumnIndex(Directory.ACCOUNT_NAME)] = accountName; 754 row[cursor.getColumnIndex(Directory.ACCOUNT_TYPE)] = accountType; 755 row[cursor.getColumnIndex(Directory.DISPLAY_NAME)] = displayName; 756 row[cursor.getColumnIndex(Directory.TYPE_RESOURCE_ID)] = typeResourceId; 757 row[cursor.getColumnIndex(Directory.EXPORT_SUPPORT)] = exportSupport; 758 row[cursor.getColumnIndex(Directory.SHORTCUT_SUPPORT)] = shortcutSupport; 759 row[cursor.getColumnIndex(Directory.PHOTO_SUPPORT)] = photoSupport; 760 cursor.addRow(row); 761 } 762 assertDirectoryRow(Cursor cursor, String packageName, String authority, String accountName, String accountType, String displayName, int typeResourceId, int exportSupport, int shortcutSupport, int photoSupport)763 protected void assertDirectoryRow(Cursor cursor, String packageName, String authority, 764 String accountName, String accountType, String displayName, int typeResourceId, 765 int exportSupport, int shortcutSupport, int photoSupport) { 766 ContentValues values = new ContentValues(); 767 values.put(Directory.PACKAGE_NAME, packageName); 768 values.put(Directory.DIRECTORY_AUTHORITY, authority); 769 values.put(Directory.ACCOUNT_NAME, accountName); 770 values.put(Directory.ACCOUNT_TYPE, accountType); 771 values.put(Directory.DISPLAY_NAME, displayName); 772 if (typeResourceId >= 0) { 773 values.put(Directory.TYPE_RESOURCE_ID, typeResourceId); 774 } 775 values.put(Directory.EXPORT_SUPPORT, exportSupport); 776 values.put(Directory.SHORTCUT_SUPPORT, shortcutSupport); 777 values.put(Directory.PHOTO_SUPPORT, photoSupport); 778 779 assertCursorValues(cursor, values); 780 } 781 } 782