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