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