ContactDirectoryManagerTest.java revision bf732767b4d4d7104e4723bda7d3b0eb0f909997
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.ProviderInfo;
27import android.database.Cursor;
28import android.database.MatrixCursor;
29import android.net.Uri;
30import android.os.Bundle;
31import android.provider.ContactsContract;
32import android.provider.ContactsContract.AggregationExceptions;
33import android.provider.ContactsContract.Contacts;
34import android.provider.ContactsContract.Directory;
35import android.provider.ContactsContract.RawContacts;
36import android.test.mock.MockContentProvider;
37import android.test.suitebuilder.annotation.LargeTest;
38
39/**
40 * Unit tests for {@link ContactDirectoryManager}. Run the test like this:
41 * <code>
42 *   adb shell am instrument -e class com.android.providers.contacts.ContactDirectoryManagerTest
43 *       -w com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
44 * </code>
45 */
46@LargeTest
47public class ContactDirectoryManagerTest extends BaseContactsProvider2Test {
48
49    private ContactsMockPackageManager mPackageManager;
50
51    private ContactsProvider2 mProvider;
52
53    private ContactDirectoryManager mDirectoryManager;
54
55    public static class MockContactDirectoryProvider extends MockContentProvider {
56
57        private String mAuthority;
58
59        private MatrixCursor mResponse;
60
61        @Override
62        public void attachInfo(Context context, ProviderInfo info) {
63            mAuthority = info.authority;
64        }
65
66        public MatrixCursor createResponseCursor() {
67            mResponse = new MatrixCursor(
68                    new String[] { Directory.ACCOUNT_NAME, Directory.ACCOUNT_TYPE,
69                            Directory.DISPLAY_NAME, Directory.TYPE_RESOURCE_ID,
70                            Directory.EXPORT_SUPPORT, Directory.SHORTCUT_SUPPORT,
71                            Directory.PHOTO_SUPPORT });
72
73            return mResponse;
74        }
75
76        @Override
77        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
78                String sortOrder) {
79
80            if (uri.toString().equals("content://" + mAuthority + "/directories")) {
81                return mResponse;
82            } else if (uri.toString().startsWith("content://" + mAuthority + "/contacts")) {
83                MatrixCursor cursor = new MatrixCursor(
84                        new String[] { "projection", "selection", "selectionArgs", "sortOrder",
85                                "accountName", "accountType"});
86                cursor.addRow(new Object[] {
87                    Lists.newArrayList(projection).toString(),
88                    selection,
89                    Lists.newArrayList(selectionArgs).toString(),
90                    sortOrder,
91                    uri.getQueryParameter(RawContacts.ACCOUNT_NAME),
92                    uri.getQueryParameter(RawContacts.ACCOUNT_TYPE),
93                });
94                return cursor;
95            } else if (uri.toString().startsWith(
96                    "content://" + mAuthority + "/aggregation_exceptions")) {
97                return new MatrixCursor(projection);
98            }
99
100            fail("Unexpected uri: " + uri);
101            return null;
102        }
103    }
104
105    @Override
106    public void setUp() throws Exception {
107        super.setUp();
108
109        mProvider = (ContactsProvider2) getProvider();
110        mDirectoryManager = mProvider.getContactDirectoryManager();
111
112        mPackageManager = (ContactsMockPackageManager) getProvider()
113                .getContext().getPackageManager();
114    }
115
116    public void testScanAllProviders() throws Exception {
117        mPackageManager.setInstalledPackages(
118                Lists.newArrayList(
119                        createProviderPackage("test.package1", "authority1"),
120                        createProviderPackage("test.package2", "authority2")));
121
122        MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider(
123                MockContactDirectoryProvider.class, "authority1");
124
125        MatrixCursor response1 = provider1.createResponseCursor();
126        addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1,
127                Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
128                Directory.PHOTO_SUPPORT_FULL_SIZE_ONLY);
129        addDirectoryRow(response1, "account-name2", "account-type2", "display-name2", 2,
130                Directory.EXPORT_SUPPORT_ANY_ACCOUNT, Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY,
131                Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY);
132
133        MockContactDirectoryProvider provider2 = (MockContactDirectoryProvider) addProvider(
134                MockContactDirectoryProvider.class, "authority2");
135
136        MatrixCursor response2 = provider2.createResponseCursor();
137        addDirectoryRow(response2, "account-name3", "account-type3", "display-name3", 3,
138                Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, Directory.SHORTCUT_SUPPORT_FULL,
139                Directory.PHOTO_SUPPORT_FULL);
140
141        mDirectoryManager.scanAllPackages();
142
143        Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null);
144        assertEquals(5, cursor.getCount());
145
146        cursor.moveToPosition(2);
147        assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1",
148                "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
149                Directory.PHOTO_SUPPORT_FULL_SIZE_ONLY);
150
151        cursor.moveToNext();
152        assertDirectoryRow(cursor, "test.package1", "authority1", "account-name2", "account-type2",
153                "display-name2", 2, Directory.EXPORT_SUPPORT_ANY_ACCOUNT,
154                Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY, Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY);
155
156        cursor.moveToNext();
157        assertDirectoryRow(cursor, "test.package2", "authority2", "account-name3", "account-type3",
158                "display-name3", 3, Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY,
159                Directory.SHORTCUT_SUPPORT_FULL, Directory.PHOTO_SUPPORT_FULL);
160
161        cursor.close();
162    }
163
164    public void testPackageInstalled() throws Exception {
165        mPackageManager.setInstalledPackages(
166                Lists.newArrayList(createProviderPackage("test.package1", "authority1")));
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);
175
176        mDirectoryManager.scanAllPackages();
177
178        // At this point the manager has discovered a single directory (plus two
179        // standard ones).
180        Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null);
181        assertEquals(3, cursor.getCount());
182        cursor.close();
183
184        // Pretend to install another package
185        MockContactDirectoryProvider provider2 = (MockContactDirectoryProvider) addProvider(
186                MockContactDirectoryProvider.class, "authority2");
187
188        MatrixCursor response2 = provider2.createResponseCursor();
189        addDirectoryRow(response2, "account-name3", "account-type3", "display-name3", 3,
190                Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, Directory.SHORTCUT_SUPPORT_FULL,
191                Directory.PHOTO_SUPPORT_FULL);
192
193        mPackageManager.getInstalledPackages(0).add(
194                createProviderPackage("test.package2", "authority2"));
195
196        mProvider.onPackageChanged("test.package2");
197
198        cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null);
199        assertEquals(4, cursor.getCount());
200
201        cursor.moveToPosition(2);
202        assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1",
203                "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
204                Directory.PHOTO_SUPPORT_FULL);
205
206        cursor.moveToNext();
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        cursor.close();
212    }
213
214    public void testPackageUninstalled() throws Exception {
215        mPackageManager.setInstalledPackages(
216                Lists.newArrayList(
217                        createProviderPackage("test.package1", "authority1"),
218                        createProviderPackage("test.package2", "authority2")));
219
220        MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider(
221                MockContactDirectoryProvider.class, "authority1");
222
223        MatrixCursor response1 = provider1.createResponseCursor();
224        addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1,
225                Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
226                Directory.PHOTO_SUPPORT_NONE);
227
228        MockContactDirectoryProvider provider2 = (MockContactDirectoryProvider) addProvider(
229                MockContactDirectoryProvider.class, "authority2");
230
231        MatrixCursor response2 = provider2.createResponseCursor();
232        addDirectoryRow(response2, "account-name3", "account-type3", "display-name3", 3,
233                Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, Directory.SHORTCUT_SUPPORT_FULL,
234                Directory.PHOTO_SUPPORT_FULL);
235
236        mDirectoryManager.scanAllPackages();
237
238        // At this point the manager has discovered two custom directories.
239        Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null);
240        assertEquals(4, cursor.getCount());
241        cursor.close();
242
243        // Pretend to uninstall one of the packages
244        mPackageManager.getInstalledPackages(0).remove(1);
245
246        mProvider.onPackageChanged("test.package2");
247
248        cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null);
249        assertEquals(3, cursor.getCount());
250
251        cursor.moveToPosition(2);
252        assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1",
253                "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
254                Directory.PHOTO_SUPPORT_NONE);
255
256        cursor.close();
257    }
258
259    public void testPackageReplaced() throws Exception {
260        mPackageManager.setInstalledPackages(
261                Lists.newArrayList(
262                        createProviderPackage("test.package1", "authority1"),
263                        createProviderPackage("test.package2", "authority2")));
264
265        MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider(
266                MockContactDirectoryProvider.class, "authority1");
267
268        MatrixCursor response1 = provider1.createResponseCursor();
269        addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1,
270                Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
271                Directory.PHOTO_SUPPORT_NONE);
272
273        MockContactDirectoryProvider provider2 = (MockContactDirectoryProvider) addProvider(
274                MockContactDirectoryProvider.class, "authority2");
275
276        MatrixCursor response2 = provider2.createResponseCursor();
277        addDirectoryRow(response2, "account-name3", "account-type3", "display-name3", 3,
278                Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY, Directory.SHORTCUT_SUPPORT_FULL,
279                Directory.PHOTO_SUPPORT_FULL);
280
281        mDirectoryManager.scanAllPackages();
282
283        // At this point the manager has discovered two custom directories.
284        Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null);
285        assertEquals(4, cursor.getCount());
286        cursor.close();
287
288        // Pretend to replace the package with a different provider inside
289        MatrixCursor response3 = provider2.createResponseCursor();
290        addDirectoryRow(response3, "account-name4", "account-type4", "display-name4", 4,
291                Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
292                Directory.PHOTO_SUPPORT_NONE);
293
294        mProvider.onPackageChanged("test.package2");
295
296        cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null);
297        assertEquals(4, cursor.getCount());
298
299        cursor.moveToPosition(2);
300        assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1",
301                "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
302                Directory.PHOTO_SUPPORT_NONE);
303
304        cursor.moveToNext();
305        assertDirectoryRow(cursor, "test.package2", "authority2", "account-name4", "account-type4",
306                "display-name4", 4, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
307                Directory.PHOTO_SUPPORT_NONE);
308
309        cursor.close();
310    }
311
312    public void testAccountRemoval() throws Exception {
313        mPackageManager.setInstalledPackages(
314                Lists.newArrayList(
315                        createProviderPackage("test.package1", "authority1"),
316                        createProviderPackage("test.package2", "authority2")));
317
318        MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider(
319                MockContactDirectoryProvider.class, "authority1");
320
321        Account[] accounts = new Account[]{
322                new Account("account-name1", "account-type1"),
323                new Account("account-name2", "account-type2")};
324        mActor.setAccounts(accounts);
325        ((ContactsProvider2)getProvider()).onAccountsUpdated(accounts);
326
327        MatrixCursor response1 = provider1.createResponseCursor();
328        addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1,
329                Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
330                Directory.PHOTO_SUPPORT_NONE);
331        addDirectoryRow(response1, "account-name2", "account-type2", "display-name2", 2,
332                Directory.EXPORT_SUPPORT_ANY_ACCOUNT, Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY,
333                Directory.PHOTO_SUPPORT_FULL_SIZE_ONLY);
334
335        mDirectoryManager.scanAllPackages();
336
337        accounts = new Account[]{new Account("account-name1", "account-type1")};
338        mActor.setAccounts(accounts);
339        ((ContactsProvider2)getProvider()).onAccountsUpdated(accounts);
340
341        Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null);
342        assertEquals(3, cursor.getCount());
343
344        cursor.moveToPosition(2);
345        assertDirectoryRow(cursor, "test.package1", "authority1", "account-name1", "account-type1",
346                "display-name1", 1, Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
347                Directory.PHOTO_SUPPORT_NONE);
348
349        cursor.close();
350    }
351
352    public void testNotifyDirectoryChange() throws Exception {
353        mPackageManager.setInstalledPackages(
354                Lists.newArrayList(createProviderPackage("test.package1", "authority1")));
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        mDirectoryManager.scanAllPackages();
365
366        // Pretend to replace the package with a different provider inside
367        MatrixCursor response2 = provider1.createResponseCursor();
368        addDirectoryRow(response2, "account-name2", "account-type2", "display-name2", 2,
369                Directory.EXPORT_SUPPORT_ANY_ACCOUNT, Directory.SHORTCUT_SUPPORT_FULL,
370                Directory.PHOTO_SUPPORT_FULL);
371
372        ContactsContract.Directory.notifyDirectoryChange(mResolver);
373
374        Cursor cursor = mResolver.query(Directory.CONTENT_URI, null, null, null, null);
375        assertEquals(3, cursor.getCount());
376
377        cursor.moveToPosition(2);
378        assertDirectoryRow(cursor, "test.package1", "authority1", "account-name2", "account-type2",
379                "display-name2", 2, Directory.EXPORT_SUPPORT_ANY_ACCOUNT,
380                Directory.SHORTCUT_SUPPORT_FULL, Directory.PHOTO_SUPPORT_FULL);
381
382        cursor.close();
383    }
384
385    public void testForwardingToDirectoryProvider() throws Exception {
386        mPackageManager.setInstalledPackages(
387                Lists.newArrayList(createProviderPackage("test.package1", "authority1")));
388
389        MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider(
390                MockContactDirectoryProvider.class, "authority1");
391
392        MatrixCursor response1 = provider1.createResponseCursor();
393        addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1,
394                Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
395                Directory.PHOTO_SUPPORT_NONE);
396
397        mDirectoryManager.scanAllPackages();
398
399        Cursor cursor = mResolver.query(
400                Directory.CONTENT_URI, new String[] { Directory._ID }, null, null, null);
401        cursor.moveToPosition(2);
402        long directoryId = cursor.getLong(0);
403        cursor.close();
404
405        Uri contentUri = Contacts.CONTENT_URI.buildUpon().appendQueryParameter(
406                ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build();
407
408        // The request should be forwarded to TestProvider, which will simply
409        // package arguments and return them to us for verification
410        cursor = mResolver.query(contentUri,
411                new String[]{"f1", "f2"}, "query", new String[]{"s1", "s2"}, "so");
412        assertNotNull(cursor);
413        assertEquals(1, cursor.getCount());
414        cursor.moveToFirst();
415        assertEquals("[f1, f2]", cursor.getString(cursor.getColumnIndex("projection")));
416        assertEquals("query", cursor.getString(cursor.getColumnIndex("selection")));
417        assertEquals("[s1, s2]", cursor.getString(cursor.getColumnIndex("selectionArgs")));
418        assertEquals("so", cursor.getString(cursor.getColumnIndex("sortOrder")));
419        assertEquals("account-name1", cursor.getString(cursor.getColumnIndex("accountName")));
420        assertEquals("account-type1", cursor.getString(cursor.getColumnIndex("accountType")));
421        cursor.close();
422    }
423
424    public void testProjectionPopulated() throws Exception {
425        mPackageManager.setInstalledPackages(
426                Lists.newArrayList(createProviderPackage("test.package1", "authority1")));
427
428        MockContactDirectoryProvider provider1 = (MockContactDirectoryProvider) addProvider(
429                MockContactDirectoryProvider.class, "authority1");
430
431        MatrixCursor response1 = provider1.createResponseCursor();
432        addDirectoryRow(response1, "account-name1", "account-type1", "display-name1", 1,
433                Directory.EXPORT_SUPPORT_NONE, Directory.SHORTCUT_SUPPORT_NONE,
434                Directory.PHOTO_SUPPORT_NONE);
435
436        mDirectoryManager.scanAllPackages();
437
438        Cursor cursor = mResolver.query(
439                Directory.CONTENT_URI, new String[] { Directory._ID }, null, null, null);
440        cursor.moveToPosition(2);
441        long directoryId = cursor.getLong(0);
442        cursor.close();
443
444        Uri contentUri = AggregationExceptions.CONTENT_URI.buildUpon().appendQueryParameter(
445                ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build();
446
447        // The request should be forwarded to TestProvider, which will return an empty cursor
448        // but the projection should be correctly populated by ContactProvider
449        assertProjection(contentUri, new String[]{
450                AggregationExceptionColumns._ID,
451                AggregationExceptions.TYPE,
452                AggregationExceptions.RAW_CONTACT_ID1,
453                AggregationExceptions.RAW_CONTACT_ID2,
454        });
455    }
456
457    protected PackageInfo createProviderPackage(String packageName, String authority) {
458        PackageInfo providerPackage = new PackageInfo();
459        providerPackage.packageName = packageName;
460        ProviderInfo providerInfo = new ProviderInfo();
461        providerInfo.packageName = providerPackage.packageName;
462        providerInfo.authority = authority;
463        providerInfo.metaData = new Bundle();
464        providerInfo.metaData.putBoolean("android.content.ContactDirectory", true);
465        providerPackage.providers = new ProviderInfo[] { providerInfo };
466        return providerPackage;
467    }
468
469    protected void addDirectoryRow(MatrixCursor cursor, String accountName, String accountType,
470            String displayName, int typeResourceId, int exportSupport, int shortcutSupport,
471            int photoSupport) {
472        Object[] row = new Object[cursor.getColumnCount()];
473        row[cursor.getColumnIndex(Directory.ACCOUNT_NAME)] = accountName;
474        row[cursor.getColumnIndex(Directory.ACCOUNT_TYPE)] = accountType;
475        row[cursor.getColumnIndex(Directory.DISPLAY_NAME)] = displayName;
476        row[cursor.getColumnIndex(Directory.TYPE_RESOURCE_ID)] = typeResourceId;
477        row[cursor.getColumnIndex(Directory.EXPORT_SUPPORT)] = exportSupport;
478        row[cursor.getColumnIndex(Directory.SHORTCUT_SUPPORT)] = shortcutSupport;
479        row[cursor.getColumnIndex(Directory.PHOTO_SUPPORT)] = photoSupport;
480        cursor.addRow(row);
481    }
482
483    protected void assertDirectoryRow(Cursor cursor, String packageName, String authority,
484            String accountName, String accountType, String displayName, int typeResourceId,
485            int exportSupport, int shortcutSupport, int photoSupport) {
486        ContentValues values = new ContentValues();
487        values.put(Directory.PACKAGE_NAME, packageName);
488        values.put(Directory.DIRECTORY_AUTHORITY, authority);
489        values.put(Directory.ACCOUNT_NAME, accountName);
490        values.put(Directory.ACCOUNT_TYPE, accountType);
491        values.put(Directory.DISPLAY_NAME, displayName);
492        values.put(Directory.TYPE_RESOURCE_ID, typeResourceId);
493        values.put(Directory.EXPORT_SUPPORT, exportSupport);
494        values.put(Directory.SHORTCUT_SUPPORT, shortcutSupport);
495        values.put(Directory.PHOTO_SUPPORT, photoSupport);
496
497        assertCursorValues(cursor, values);
498    }
499}
500