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