• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.contacts;
18 
19 import static com.android.providers.contacts.ContactsActor.PACKAGE_GREY;
20 import static com.android.providers.contacts.TestUtils.cv;
21 
22 import android.accounts.Account;
23 import android.content.ContentProvider;
24 import android.content.ContentResolver;
25 import android.content.ContentUris;
26 import android.content.ContentValues;
27 import android.content.Context;
28 import android.content.Entity;
29 import android.database.Cursor;
30 import android.database.sqlite.SQLiteDatabase;
31 import android.net.Uri;
32 import android.provider.BaseColumns;
33 import android.provider.ContactsContract;
34 import android.provider.ContactsContract.AggregationExceptions;
35 import android.provider.ContactsContract.CommonDataKinds.Email;
36 import android.provider.ContactsContract.CommonDataKinds.Event;
37 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
38 import android.provider.ContactsContract.CommonDataKinds.Identity;
39 import android.provider.ContactsContract.CommonDataKinds.Im;
40 import android.provider.ContactsContract.CommonDataKinds.Nickname;
41 import android.provider.ContactsContract.CommonDataKinds.Note;
42 import android.provider.ContactsContract.CommonDataKinds.Organization;
43 import android.provider.ContactsContract.CommonDataKinds.Phone;
44 import android.provider.ContactsContract.CommonDataKinds.Photo;
45 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
46 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
47 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
48 import android.provider.ContactsContract.Contacts;
49 import android.provider.ContactsContract.Data;
50 import android.provider.ContactsContract.Groups;
51 import android.provider.ContactsContract.RawContacts;
52 import android.provider.ContactsContract.Settings;
53 import android.provider.ContactsContract.StatusUpdates;
54 import android.provider.ContactsContract.StreamItems;
55 import android.test.MoreAsserts;
56 import android.test.mock.MockContentResolver;
57 import android.util.Log;
58 
59 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
60 import com.android.providers.contacts.testutil.CommonDatabaseUtils;
61 import com.android.providers.contacts.testutil.DataUtil;
62 import com.android.providers.contacts.testutil.RawContactUtil;
63 import com.android.providers.contacts.testutil.TestUtil;
64 import com.android.providers.contacts.util.Hex;
65 import com.android.providers.contacts.util.MockClock;
66 import com.google.android.collect.Sets;
67 
68 import java.util.ArrayList;
69 import java.util.Arrays;
70 import java.util.BitSet;
71 import java.util.Comparator;
72 import java.util.Iterator;
73 import java.util.Map;
74 import java.util.Map.Entry;
75 import java.util.Set;
76 
77 /**
78  * A common superclass for {@link ContactsProvider2}-related tests.
79  */
80 public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase {
81 
82     static final String ADD_VOICEMAIL_PERMISSION =
83             "com.android.voicemail.permission.ADD_VOICEMAIL";
84     /*
85      * Permission to allow querying voicemails
86      */
87     static final String READ_VOICEMAIL_PERMISSION =
88             "com.android.voicemail.permission.READ_VOICEMAIL";
89     /*
90      * Permission to allow deleting and updating voicemails
91      */
92     static final String WRITE_VOICEMAIL_PERMISSION =
93             "com.android.voicemail.permission.WRITE_VOICEMAIL";
94 
95     protected static final String PACKAGE = "ContactsProvider2Test";
96     public static final String READ_ONLY_ACCOUNT_TYPE =
97             SynchronousContactsProvider2.READ_ONLY_ACCOUNT_TYPE;
98 
99     protected ContactsActor mActor;
100     protected MockContentResolver mResolver;
101     protected Account mAccount = new Account("account1", "account type1");
102     protected Account mAccountTwo = new Account("account2", "account type2");
103 
104     protected final static Long NO_LONG = new Long(0);
105     protected final static String NO_STRING = new String("");
106     protected final static Account NO_ACCOUNT = new Account("a", "b");
107 
108     /**
109      * Use {@link MockClock#install()} to start using it.
110      * It'll be automatically uninstalled by {@link #tearDown()}.
111      */
112     protected static final MockClock sMockClock = new MockClock();
113 
getProviderClass()114     protected Class<? extends ContentProvider> getProviderClass() {
115         return SynchronousContactsProvider2.class;
116     }
117 
getAuthority()118     protected String getAuthority() {
119         return ContactsContract.AUTHORITY;
120     }
121 
122     @Override
setUp()123     protected void setUp() throws Exception {
124         super.setUp();
125 
126         mActor = new ContactsActor(getContext(), PACKAGE_GREY, getProviderClass(), getAuthority());
127         mResolver = mActor.resolver;
128         if (mActor.provider instanceof SynchronousContactsProvider2) {
129             getContactsProvider().wipeData();
130         }
131 
132         // Give the actor access to read/write contacts and profile data by default.
133         mActor.addPermissions(
134                 "android.permission.READ_CONTACTS",
135                 "android.permission.WRITE_CONTACTS",
136                 "android.permission.READ_SOCIAL_STREAM",
137                 "android.permission.WRITE_SOCIAL_STREAM");
138     }
139 
140     @Override
tearDown()141     protected void tearDown() throws Exception {
142         sMockClock.uninstall();
143         super.tearDown();
144     }
145 
getContactsProvider()146     public SynchronousContactsProvider2 getContactsProvider() {
147         return (SynchronousContactsProvider2) mActor.provider;
148     }
149 
getMockContext()150     public Context getMockContext() {
151         return mActor.context;
152     }
153 
addProvider(Class<? extends ContentProvider> providerClass, String authority)154     public ContentProvider addProvider(Class<? extends ContentProvider> providerClass,
155             String authority) throws Exception {
156         return mActor.addProvider(providerClass, authority);
157     }
158 
getProvider()159     public ContentProvider getProvider() {
160         return mActor.provider;
161     }
162 
setCallerIsSyncAdapter(Uri uri, Account account)163     protected Uri setCallerIsSyncAdapter(Uri uri, Account account) {
164         if (account == null) {
165             return uri;
166         }
167         final Uri.Builder builder = uri.buildUpon();
168         builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, account.name);
169         builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type);
170         builder.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true");
171         return builder.build();
172     }
173 
updateItem(Uri uri, long id, String... extras)174     protected int updateItem(Uri uri, long id, String... extras) {
175         Uri itemUri = ContentUris.withAppendedId(uri, id);
176         return updateItem(itemUri, extras);
177     }
178 
updateItem(Uri uri, String... extras)179     protected int updateItem(Uri uri, String... extras) {
180         ContentValues values = new ContentValues();
181         CommonDatabaseUtils.extrasVarArgsToValues(values, extras);
182         return mResolver.update(uri, values, null, null);
183     }
184 
createGroup(Account account, String sourceId, String title)185     protected long createGroup(Account account, String sourceId, String title) {
186         return createGroup(account, sourceId, title, 1, false, false);
187     }
188 
createGroup(Account account, String sourceId, String title, int visible)189     protected long createGroup(Account account, String sourceId, String title, int visible) {
190         return createGroup(account, sourceId, title, visible, false, false);
191     }
192 
createAutoAddGroup(Account account)193     protected long createAutoAddGroup(Account account) {
194         return createGroup(account, "auto", "auto",
195                 0 /* visible */,  true /* auto-add */, false /* fav */);
196     }
197 
createGroup(Account account, String sourceId, String title, int visible, boolean autoAdd, boolean favorite)198     protected long createGroup(Account account, String sourceId, String title,
199             int visible, boolean autoAdd, boolean favorite) {
200         ContentValues values = new ContentValues();
201         values.put(Groups.SOURCE_ID, sourceId);
202         values.put(Groups.TITLE, title);
203         values.put(Groups.GROUP_VISIBLE, visible);
204         values.put(Groups.AUTO_ADD, autoAdd ? 1 : 0);
205         values.put(Groups.FAVORITES, favorite ? 1 : 0);
206         final Uri uri = TestUtil.maybeAddAccountQueryParameters(Groups.CONTENT_URI, account);
207         return ContentUris.parseId(mResolver.insert(uri, values));
208     }
209 
createSettings(Account account, String shouldSync, String ungroupedVisible)210     protected void createSettings(Account account, String shouldSync, String ungroupedVisible) {
211         createSettings(new AccountWithDataSet(account.name, account.type, null),
212                 shouldSync, ungroupedVisible);
213     }
214 
createSettings(AccountWithDataSet account, String shouldSync, String ungroupedVisible)215     protected void createSettings(AccountWithDataSet account, String shouldSync,
216             String ungroupedVisible) {
217         ContentValues values = new ContentValues();
218         values.put(Settings.ACCOUNT_NAME, account.getAccountName());
219         values.put(Settings.ACCOUNT_TYPE, account.getAccountType());
220         if (account.getDataSet() != null) {
221             values.put(Settings.DATA_SET, account.getDataSet());
222         }
223         values.put(Settings.SHOULD_SYNC, shouldSync);
224         values.put(Settings.UNGROUPED_VISIBLE, ungroupedVisible);
225         mResolver.insert(Settings.CONTENT_URI, values);
226     }
227 
insertOrganization(long rawContactId, ContentValues values)228     protected Uri insertOrganization(long rawContactId, ContentValues values) {
229         return insertOrganization(rawContactId, values, false, false);
230     }
231 
insertOrganization(long rawContactId, ContentValues values, boolean primary)232     protected Uri insertOrganization(long rawContactId, ContentValues values, boolean primary) {
233         return insertOrganization(rawContactId, values, primary, false);
234     }
235 
insertOrganization(long rawContactId, ContentValues values, boolean primary, boolean superPrimary)236     protected Uri insertOrganization(long rawContactId, ContentValues values, boolean primary,
237             boolean superPrimary) {
238         values.put(Data.RAW_CONTACT_ID, rawContactId);
239         values.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
240         values.put(Organization.TYPE, Organization.TYPE_WORK);
241         if (primary) {
242             values.put(Data.IS_PRIMARY, 1);
243         }
244         if (superPrimary) {
245             values.put(Data.IS_SUPER_PRIMARY, 1);
246         }
247 
248         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
249         return resultUri;
250     }
251 
insertPhoneNumber(long rawContactId, String phoneNumber)252     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber) {
253         return insertPhoneNumber(rawContactId, phoneNumber, false);
254     }
255 
insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary)256     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary) {
257         return insertPhoneNumber(rawContactId, phoneNumber, primary, false, Phone.TYPE_HOME);
258     }
259 
insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary, boolean superPrimary)260     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary,
261             boolean superPrimary) {
262         return insertPhoneNumber(rawContactId, phoneNumber, primary, superPrimary, Phone.TYPE_HOME);
263     }
264 
insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary, int type)265     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary,
266             int type) {
267         return insertPhoneNumber(rawContactId, phoneNumber, primary, false, type);
268     }
269 
insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary, boolean superPrimary, int type)270     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary,
271             boolean superPrimary, int type) {
272         ContentValues values = new ContentValues();
273         values.put(Data.RAW_CONTACT_ID, rawContactId);
274         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
275         values.put(Phone.NUMBER, phoneNumber);
276         values.put(Phone.TYPE, type);
277         if (primary) {
278             values.put(Data.IS_PRIMARY, 1);
279         }
280         if (superPrimary) {
281             values.put(Data.IS_SUPER_PRIMARY, 1);
282         }
283 
284         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
285         return resultUri;
286     }
287 
insertEmail(long rawContactId, String email)288     protected Uri insertEmail(long rawContactId, String email) {
289         return insertEmail(rawContactId, email, false);
290     }
291 
insertEmail(long rawContactId, String email, boolean primary)292     protected Uri insertEmail(long rawContactId, String email, boolean primary) {
293         return insertEmail(rawContactId, email, primary, Email.TYPE_HOME, null);
294     }
295 
insertEmail(long rawContactId, String email, boolean primary, boolean superPrimary)296     protected Uri insertEmail(long rawContactId, String email, boolean primary,
297             boolean superPrimary) {
298         return insertEmail(rawContactId, email, primary, superPrimary, Email.TYPE_HOME, null);
299     }
300 
insertEmail(long rawContactId, String email, boolean primary, int type, String label)301     protected Uri insertEmail(long rawContactId, String email, boolean primary, int type,
302             String label) {
303         return insertEmail(rawContactId, email, primary, false, type, label);
304     }
305 
insertEmail(long rawContactId, String email, boolean primary, boolean superPrimary, int type, String label)306     protected Uri insertEmail(long rawContactId, String email, boolean primary,
307             boolean superPrimary, int type,  String label) {
308         ContentValues values = new ContentValues();
309         values.put(Data.RAW_CONTACT_ID, rawContactId);
310         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
311         values.put(Email.DATA, email);
312         values.put(Email.TYPE, type);
313         values.put(Email.LABEL, label);
314         if (primary) {
315             values.put(Data.IS_PRIMARY, 1);
316         }
317         if (superPrimary) {
318             values.put(Data.IS_SUPER_PRIMARY, 1);
319         }
320 
321         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
322         return resultUri;
323     }
324 
insertSipAddress(long rawContactId, String sipAddress)325     protected Uri insertSipAddress(long rawContactId, String sipAddress) {
326         return insertSipAddress(rawContactId, sipAddress, false);
327     }
328 
insertSipAddress(long rawContactId, String sipAddress, boolean primary)329     protected Uri insertSipAddress(long rawContactId, String sipAddress, boolean primary) {
330         ContentValues values = new ContentValues();
331         values.put(Data.RAW_CONTACT_ID, rawContactId);
332         values.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
333         values.put(SipAddress.SIP_ADDRESS, sipAddress);
334         if (primary) {
335             values.put(Data.IS_PRIMARY, 1);
336         }
337 
338         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
339         return resultUri;
340     }
341 
insertNickname(long rawContactId, String nickname)342     protected Uri insertNickname(long rawContactId, String nickname) {
343         ContentValues values = new ContentValues();
344         values.put(Data.RAW_CONTACT_ID, rawContactId);
345         values.put(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
346         values.put(Nickname.NAME, nickname);
347         values.put(Nickname.TYPE, Nickname.TYPE_OTHER_NAME);
348 
349         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
350         return resultUri;
351     }
352 
insertPostalAddress(long rawContactId, String formattedAddress)353     protected Uri insertPostalAddress(long rawContactId, String formattedAddress) {
354         ContentValues values = new ContentValues();
355         values.put(Data.RAW_CONTACT_ID, rawContactId);
356         values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
357         values.put(StructuredPostal.FORMATTED_ADDRESS, formattedAddress);
358 
359         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
360         return resultUri;
361     }
362 
insertPostalAddress(long rawContactId, ContentValues values)363     protected Uri insertPostalAddress(long rawContactId, ContentValues values) {
364         values.put(Data.RAW_CONTACT_ID, rawContactId);
365         values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
366         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
367         return resultUri;
368     }
369 
insertPhoto(long rawContactId)370     protected Uri insertPhoto(long rawContactId) {
371         ContentValues values = new ContentValues();
372         values.put(Data.RAW_CONTACT_ID, rawContactId);
373         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
374         values.put(Photo.PHOTO, loadTestPhoto());
375         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
376         return resultUri;
377     }
378 
insertPhoto(long rawContactId, int resourceId)379     protected Uri insertPhoto(long rawContactId, int resourceId) {
380         ContentValues values = new ContentValues();
381         values.put(Data.RAW_CONTACT_ID, rawContactId);
382         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
383         values.put(Photo.PHOTO, loadPhotoFromResource(resourceId, PhotoSize.ORIGINAL));
384         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
385         return resultUri;
386     }
387 
insertGroupMembership(long rawContactId, String sourceId)388     protected Uri insertGroupMembership(long rawContactId, String sourceId) {
389         ContentValues values = new ContentValues();
390         values.put(Data.RAW_CONTACT_ID, rawContactId);
391         values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
392         values.put(GroupMembership.GROUP_SOURCE_ID, sourceId);
393         return mResolver.insert(Data.CONTENT_URI, values);
394     }
395 
insertGroupMembership(long rawContactId, Long groupId)396     protected Uri insertGroupMembership(long rawContactId, Long groupId) {
397         ContentValues values = new ContentValues();
398         values.put(Data.RAW_CONTACT_ID, rawContactId);
399         values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
400         values.put(GroupMembership.GROUP_ROW_ID, groupId);
401         return mResolver.insert(Data.CONTENT_URI, values);
402     }
403 
removeGroupMemberships(long rawContactId)404     public void removeGroupMemberships(long rawContactId) {
405         mResolver.delete(Data.CONTENT_URI,
406                 Data.MIMETYPE + "=? AND " + GroupMembership.RAW_CONTACT_ID + "=?",
407                 new String[] { GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(rawContactId) });
408     }
409 
insertStatusUpdate(int protocol, String customProtocol, String handle, int presence, String status, int chatMode)410     protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle,
411             int presence, String status, int chatMode) {
412         return insertStatusUpdate(protocol, customProtocol, handle, presence, status, chatMode,
413                 false);
414     }
415 
insertStatusUpdate(int protocol, String customProtocol, String handle, int presence, String status, int chatMode, boolean isUserProfile)416     protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle,
417             int presence, String status, int chatMode, boolean isUserProfile) {
418         return insertStatusUpdate(protocol, customProtocol, handle, presence, status, 0, chatMode,
419                 isUserProfile);
420     }
421 
insertStatusUpdate(int protocol, String customProtocol, String handle, int presence, String status, long timestamp, int chatMode, boolean isUserProfile)422     protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle,
423             int presence, String status, long timestamp, int chatMode, boolean isUserProfile) {
424         ContentValues values = new ContentValues();
425         values.put(StatusUpdates.PROTOCOL, protocol);
426         values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
427         values.put(StatusUpdates.IM_HANDLE, handle);
428         return insertStatusUpdate(values, presence, status, timestamp, chatMode, isUserProfile);
429     }
430 
insertStatusUpdate( long dataId, int presence, String status, long timestamp, int chatMode)431     protected Uri insertStatusUpdate(
432             long dataId, int presence, String status, long timestamp, int chatMode) {
433         return insertStatusUpdate(dataId, presence, status, timestamp, chatMode, false);
434     }
435 
insertStatusUpdate( long dataId, int presence, String status, long timestamp, int chatMode, boolean isUserProfile)436     protected Uri insertStatusUpdate(
437             long dataId, int presence, String status, long timestamp, int chatMode,
438             boolean isUserProfile) {
439         ContentValues values = new ContentValues();
440         values.put(StatusUpdates.DATA_ID, dataId);
441         return insertStatusUpdate(values, presence, status, timestamp, chatMode, isUserProfile);
442     }
443 
insertStatusUpdate( ContentValues values, int presence, String status, long timestamp, int chatMode, boolean isUserProfile)444     private Uri insertStatusUpdate(
445             ContentValues values, int presence, String status, long timestamp, int chatMode,
446             boolean isUserProfile) {
447         if (presence != 0) {
448             values.put(StatusUpdates.PRESENCE, presence);
449             values.put(StatusUpdates.CHAT_CAPABILITY, chatMode);
450         }
451         if (status != null) {
452             values.put(StatusUpdates.STATUS, status);
453         }
454         if (timestamp != 0) {
455             values.put(StatusUpdates.STATUS_TIMESTAMP, timestamp);
456         }
457 
458         Uri insertUri = isUserProfile
459                 ? StatusUpdates.PROFILE_CONTENT_URI
460                 : StatusUpdates.CONTENT_URI;
461         Uri resultUri = mResolver.insert(insertUri, values);
462         return resultUri;
463     }
464 
insertStreamItem(long rawContactId, ContentValues values, Account account)465     protected Uri insertStreamItem(long rawContactId, ContentValues values, Account account) {
466         return mResolver.insert(
467                 TestUtil.maybeAddAccountQueryParameters(Uri.withAppendedPath(
468                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
469                         RawContacts.StreamItems.CONTENT_DIRECTORY), account),
470                 values);
471     }
472 
insertStreamItemPhoto(long streamItemId, ContentValues values, Account account)473     protected Uri insertStreamItemPhoto(long streamItemId, ContentValues values, Account account) {
474         return mResolver.insert(
475                 TestUtil.maybeAddAccountQueryParameters(Uri.withAppendedPath(
476                         ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
477                         StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), account),
478                 values);
479     }
480 
insertImHandle(long rawContactId, int protocol, String customProtocol, String handle)481     protected Uri insertImHandle(long rawContactId, int protocol, String customProtocol,
482             String handle) {
483         ContentValues values = new ContentValues();
484         values.put(Data.RAW_CONTACT_ID, rawContactId);
485         values.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
486         values.put(Im.PROTOCOL, protocol);
487         values.put(Im.CUSTOM_PROTOCOL, customProtocol);
488         values.put(Im.DATA, handle);
489         values.put(Im.TYPE, Im.TYPE_HOME);
490 
491         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
492         return resultUri;
493     }
494 
insertEvent(long rawContactId, int type, String date)495     protected Uri insertEvent(long rawContactId, int type, String date) {
496         ContentValues values = new ContentValues();
497         values.put(Data.RAW_CONTACT_ID, rawContactId);
498         values.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
499         values.put(Event.TYPE, type);
500         values.put(Event.START_DATE, date);
501         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
502         return resultUri;
503     }
504 
insertNote(long rawContactId, String note)505     protected Uri insertNote(long rawContactId, String note) {
506         ContentValues values = new ContentValues();
507         values.put(Data.RAW_CONTACT_ID, rawContactId);
508         values.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
509         values.put(Note.NOTE, note);
510         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
511         return resultUri;
512     }
513 
insertIdentity(long rawContactId, String identity, String namespace)514     protected Uri insertIdentity(long rawContactId, String identity, String namespace) {
515         ContentValues values = new ContentValues();
516         values.put(Data.RAW_CONTACT_ID, rawContactId);
517         values.put(Data.MIMETYPE, Identity.CONTENT_ITEM_TYPE);
518         values.put(Identity.NAMESPACE, namespace);
519         values.put(Identity.IDENTITY, identity);
520 
521         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
522         return resultUri;
523     }
524 
setContactAccount(long rawContactId, String accountType, String accountName)525     protected void setContactAccount(long rawContactId, String accountType, String accountName) {
526         ContentValues values = new ContentValues();
527         values.put(RawContacts.ACCOUNT_TYPE, accountType);
528         values.put(RawContacts.ACCOUNT_NAME, accountName);
529 
530         mResolver.update(ContentUris.withAppendedId(
531                 RawContacts.CONTENT_URI, rawContactId), values, null, null);
532     }
533 
setAggregationException(int type, long rawContactId1, long rawContactId2)534     protected void setAggregationException(int type, long rawContactId1, long rawContactId2) {
535         ContentValues values = new ContentValues();
536         values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
537         values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
538         values.put(AggregationExceptions.TYPE, type);
539         assertEquals(1, mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null));
540     }
541 
setRawContactCustomization(long rawContactId, int starred, int sendToVoiceMail)542     protected void setRawContactCustomization(long rawContactId, int starred, int sendToVoiceMail) {
543         ContentValues values = new ContentValues();
544 
545         values.put(RawContacts.STARRED, starred);
546         values.put(RawContacts.SEND_TO_VOICEMAIL, sendToVoiceMail);
547 
548         assertEquals(1, mResolver.update(ContentUris.withAppendedId(
549                 RawContacts.CONTENT_URI, rawContactId), values, null, null));
550     }
551 
markInvisible(long contactId)552     protected void markInvisible(long contactId) {
553         // There's no api for this, so we just tweak the DB directly.
554         SQLiteDatabase db = ((ContactsProvider2) getProvider()).getDatabaseHelper()
555                 .getWritableDatabase();
556         db.execSQL("DELETE FROM " + Tables.DEFAULT_DIRECTORY +
557                 " WHERE " + BaseColumns._ID + "=" + contactId);
558     }
559 
queryRawContact(long rawContactId)560     protected Cursor queryRawContact(long rawContactId) {
561         return mResolver.query(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
562                 null, null, null, null);
563     }
564 
queryContact(long contactId)565     protected Cursor queryContact(long contactId) {
566         return mResolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
567                 null, null, null, null);
568     }
569 
queryContact(long contactId, String[] projection)570     protected Cursor queryContact(long contactId, String[] projection) {
571         return mResolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
572                 projection, null, null, null);
573     }
574 
getContactUriForRawContact(long rawContactId)575     protected Uri getContactUriForRawContact(long rawContactId) {
576         return ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId));
577     }
578 
queryContactId(long rawContactId)579     protected long queryContactId(long rawContactId) {
580         Cursor c = queryRawContact(rawContactId);
581         assertTrue(c.moveToFirst());
582         long contactId = c.getLong(c.getColumnIndex(RawContacts.CONTACT_ID));
583         c.close();
584         return contactId;
585     }
586 
queryPhotoId(long contactId)587     protected long queryPhotoId(long contactId) {
588         Cursor c = queryContact(contactId);
589         assertTrue(c.moveToFirst());
590         long photoId = c.getInt(c.getColumnIndex(Contacts.PHOTO_ID));
591         c.close();
592         return photoId;
593     }
594 
queryPhotoFileId(long contactId)595     protected long queryPhotoFileId(long contactId) {
596         return getStoredLongValue(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
597                 Contacts.PHOTO_FILE_ID);
598     }
599 
queryRawContactIsStarred(long rawContactId)600     protected boolean queryRawContactIsStarred(long rawContactId) {
601         Cursor c = queryRawContact(rawContactId);
602         try {
603             assertTrue(c.moveToFirst());
604             return c.getLong(c.getColumnIndex(RawContacts.STARRED)) != 0;
605         } finally {
606             c.close();
607         }
608     }
609 
queryDisplayName(long contactId)610     protected String queryDisplayName(long contactId) {
611         Cursor c = queryContact(contactId);
612         assertTrue(c.moveToFirst());
613         String displayName = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME));
614         c.close();
615         return displayName;
616     }
617 
queryLookupKey(long contactId)618     protected String queryLookupKey(long contactId) {
619         Cursor c = queryContact(contactId);
620         assertTrue(c.moveToFirst());
621         String lookupKey = c.getString(c.getColumnIndex(Contacts.LOOKUP_KEY));
622         c.close();
623         return lookupKey;
624     }
625 
assertAggregated(long rawContactId1, long rawContactId2)626     protected void assertAggregated(long rawContactId1, long rawContactId2) {
627         long contactId1 = queryContactId(rawContactId1);
628         long contactId2 = queryContactId(rawContactId2);
629         assertTrue(contactId1 == contactId2);
630     }
631 
assertAggregated(long rawContactId1, long rawContactId2, String expectedDisplayName)632     protected void assertAggregated(long rawContactId1, long rawContactId2,
633             String expectedDisplayName) {
634         long contactId1 = queryContactId(rawContactId1);
635         long contactId2 = queryContactId(rawContactId2);
636         assertTrue(contactId1 == contactId2);
637 
638         String displayName = queryDisplayName(contactId1);
639         assertEquals(expectedDisplayName, displayName);
640     }
641 
assertNotAggregated(long rawContactId1, long rawContactId2)642     protected void assertNotAggregated(long rawContactId1, long rawContactId2) {
643         long contactId1 = queryContactId(rawContactId1);
644         long contactId2 = queryContactId(rawContactId2);
645         assertTrue(contactId1 != contactId2);
646     }
647 
assertStructuredName(long rawContactId, String prefix, String givenName, String middleName, String familyName, String suffix)648     protected void assertStructuredName(long rawContactId, String prefix, String givenName,
649             String middleName, String familyName, String suffix) {
650         Uri uri = Uri.withAppendedPath(
651                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
652                 RawContacts.Data.CONTENT_DIRECTORY);
653 
654         final String[] projection = new String[] {
655                 StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
656                 StructuredName.FAMILY_NAME, StructuredName.SUFFIX
657         };
658 
659         Cursor c = mResolver.query(uri, projection, Data.MIMETYPE + "='"
660                 + StructuredName.CONTENT_ITEM_TYPE + "'", null, null);
661 
662         assertTrue(c.moveToFirst());
663         assertEquals(prefix, c.getString(0));
664         assertEquals(givenName, c.getString(1));
665         assertEquals(middleName, c.getString(2));
666         assertEquals(familyName, c.getString(3));
667         assertEquals(suffix, c.getString(4));
668         c.close();
669     }
670 
assertSingleGroup(Long rowId, Account account, String sourceId, String title)671     protected long assertSingleGroup(Long rowId, Account account, String sourceId, String title) {
672         Cursor c = mResolver.query(Groups.CONTENT_URI, null, null, null, null);
673         try {
674             assertTrue(c.moveToNext());
675             long actualRowId = assertGroup(c, rowId, account, sourceId, title);
676             assertFalse(c.moveToNext());
677             return actualRowId;
678         } finally {
679             c.close();
680         }
681     }
682 
assertSingleGroupMembership(Long rowId, Long rawContactId, Long groupRowId, String sourceId)683     protected long assertSingleGroupMembership(Long rowId, Long rawContactId, Long groupRowId,
684             String sourceId) {
685         Cursor c = mResolver.query(ContactsContract.Data.CONTENT_URI, null, null, null, null);
686         try {
687             assertTrue(c.moveToNext());
688             long actualRowId = assertGroupMembership(c, rowId, rawContactId, groupRowId, sourceId);
689             assertFalse(c.moveToNext());
690             return actualRowId;
691         } finally {
692             c.close();
693         }
694     }
695 
assertGroupMembership(Cursor c, Long rowId, Long rawContactId, Long groupRowId, String sourceId)696     protected long assertGroupMembership(Cursor c, Long rowId, Long rawContactId, Long groupRowId,
697             String sourceId) {
698         assertNullOrEquals(c, rowId, Data._ID);
699         assertNullOrEquals(c, rawContactId, GroupMembership.RAW_CONTACT_ID);
700         assertNullOrEquals(c, groupRowId, GroupMembership.GROUP_ROW_ID);
701         assertNullOrEquals(c, sourceId, GroupMembership.GROUP_SOURCE_ID);
702         return c.getLong(c.getColumnIndexOrThrow("_id"));
703     }
704 
assertGroup(Cursor c, Long rowId, Account account, String sourceId, String title)705     protected long assertGroup(Cursor c, Long rowId, Account account, String sourceId, String title) {
706         assertNullOrEquals(c, rowId, Groups._ID);
707         assertNullOrEquals(c, account);
708         assertNullOrEquals(c, sourceId, Groups.SOURCE_ID);
709         assertNullOrEquals(c, title, Groups.TITLE);
710         return c.getLong(c.getColumnIndexOrThrow("_id"));
711     }
712 
assertNullOrEquals(Cursor c, Account account)713     private void assertNullOrEquals(Cursor c, Account account) {
714         if (account == NO_ACCOUNT) {
715             return;
716         }
717         if (account == null) {
718             assertTrue(c.isNull(c.getColumnIndexOrThrow(Groups.ACCOUNT_NAME)));
719             assertTrue(c.isNull(c.getColumnIndexOrThrow(Groups.ACCOUNT_TYPE)));
720         } else {
721             assertEquals(account.name, c.getString(c.getColumnIndexOrThrow(Groups.ACCOUNT_NAME)));
722             assertEquals(account.type, c.getString(c.getColumnIndexOrThrow(Groups.ACCOUNT_TYPE)));
723         }
724     }
725 
assertNullOrEquals(Cursor c, Long value, String columnName)726     private void assertNullOrEquals(Cursor c, Long value, String columnName) {
727         if (value != NO_LONG) {
728             if (value == null) assertTrue(c.isNull(c.getColumnIndexOrThrow(columnName)));
729             else assertEquals((long) value, c.getLong(c.getColumnIndexOrThrow(columnName)));
730         }
731     }
732 
assertNullOrEquals(Cursor c, String value, String columnName)733     private void assertNullOrEquals(Cursor c, String value, String columnName) {
734         if (value != NO_STRING) {
735             if (value == null) assertTrue(c.isNull(c.getColumnIndexOrThrow(columnName)));
736             else assertEquals(value, c.getString(c.getColumnIndexOrThrow(columnName)));
737         }
738     }
739 
assertSuperPrimary(Long dataId, boolean isSuperPrimary)740     protected void assertSuperPrimary(Long dataId, boolean isSuperPrimary) {
741         final String[] projection = new String[]{Data.MIMETYPE, Data._ID, Data.IS_SUPER_PRIMARY};
742         Cursor c = mResolver.query(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
743                 projection, null, null, null);
744 
745         c.moveToFirst();
746         if (isSuperPrimary) {
747             assertEquals(1, c.getInt(c.getColumnIndexOrThrow(Data.IS_SUPER_PRIMARY)));
748         } else {
749             assertEquals(0, c.getInt(c.getColumnIndexOrThrow(Data.IS_SUPER_PRIMARY)));
750         }
751 
752     }
753 
assertDataRow(ContentValues actual, String expectedMimetype, Object... expectedArguments)754     protected void assertDataRow(ContentValues actual, String expectedMimetype,
755             Object... expectedArguments) {
756         assertEquals(actual.toString(), expectedMimetype, actual.getAsString(Data.MIMETYPE));
757         for (int i = 0; i < expectedArguments.length; i += 2) {
758             String columnName = (String) expectedArguments[i];
759             Object expectedValue = expectedArguments[i + 1];
760             if (expectedValue instanceof Uri) {
761                 expectedValue = ContentUris.parseId((Uri) expectedValue);
762             }
763             if (expectedValue == null) {
764                 assertNull(actual.toString(), actual.get(columnName));
765             }
766             if (expectedValue instanceof Long) {
767                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
768                         expectedValue, actual.getAsLong(columnName));
769             } else if (expectedValue instanceof Integer) {
770                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
771                         expectedValue, actual.getAsInteger(columnName));
772             } else if (expectedValue instanceof String) {
773                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
774                         expectedValue, actual.getAsString(columnName));
775             } else {
776                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
777                         expectedValue, actual.get(columnName));
778             }
779         }
780     }
781 
assertNoRowsAndClose(Cursor c)782     protected void assertNoRowsAndClose(Cursor c) {
783         try {
784             assertFalse(c.moveToNext());
785         } finally {
786             c.close();
787         }
788     }
789 
790     protected static class IdComparator implements Comparator<ContentValues> {
791         @Override
compare(ContentValues o1, ContentValues o2)792         public int compare(ContentValues o1, ContentValues o2) {
793             long id1 = o1.getAsLong(ContactsContract.Data._ID);
794             long id2 = o2.getAsLong(ContactsContract.Data._ID);
795             if (id1 == id2) return 0;
796             return (id1 < id2) ? -1 : 1;
797         }
798     }
799 
asSortedContentValuesArray( ArrayList<Entity.NamedContentValues> subValues)800     protected ContentValues[] asSortedContentValuesArray(
801             ArrayList<Entity.NamedContentValues> subValues) {
802         ContentValues[] result = new ContentValues[subValues.size()];
803         int i = 0;
804         for (Entity.NamedContentValues subValue : subValues) {
805             result[i] = subValue.values;
806             i++;
807         }
808         Arrays.sort(result, new IdComparator());
809         return result;
810     }
811 
assertDirty(Uri uri, boolean state)812     protected void assertDirty(Uri uri, boolean state) {
813         Cursor c = mResolver.query(uri, new String[]{"dirty"}, null, null, null);
814         assertTrue(c.moveToNext());
815         assertEquals(state, c.getLong(0) != 0);
816         assertFalse(c.moveToNext());
817         c.close();
818     }
819 
getVersion(Uri uri)820     protected long getVersion(Uri uri) {
821         Cursor c = mResolver.query(uri, new String[]{"version"}, null, null, null);
822         assertTrue(c.moveToNext());
823         long version = c.getLong(0);
824         assertFalse(c.moveToNext());
825         c.close();
826         return version;
827     }
828 
clearDirty(Uri uri)829     protected void clearDirty(Uri uri) {
830         ContentValues values = new ContentValues();
831         values.put("dirty", 0);
832         mResolver.update(uri, values, null, null);
833     }
834 
storeValue(Uri contentUri, long id, String column, String value)835     protected void storeValue(Uri contentUri, long id, String column, String value) {
836         storeValue(ContentUris.withAppendedId(contentUri, id), column, value);
837     }
838 
storeValue(Uri contentUri, String column, String value)839     protected void storeValue(Uri contentUri, String column, String value) {
840         ContentValues values = new ContentValues();
841         values.put(column, value);
842 
843         mResolver.update(contentUri, values, null, null);
844     }
845 
storeValue(Uri contentUri, long id, String column, long value)846     protected void storeValue(Uri contentUri, long id, String column, long value) {
847         storeValue(ContentUris.withAppendedId(contentUri, id), column, value);
848     }
849 
storeValue(Uri contentUri, String column, long value)850     protected void storeValue(Uri contentUri, String column, long value) {
851         ContentValues values = new ContentValues();
852         values.put(column, value);
853 
854         mResolver.update(contentUri, values, null, null);
855     }
856 
assertStoredValue(Uri contentUri, long id, String column, Object expectedValue)857     protected void assertStoredValue(Uri contentUri, long id, String column, Object expectedValue) {
858         assertStoredValue(ContentUris.withAppendedId(contentUri, id), column, expectedValue);
859     }
860 
assertStoredValue(Uri rowUri, String column, Object expectedValue)861     protected void assertStoredValue(Uri rowUri, String column, Object expectedValue) {
862         String value = getStoredValue(rowUri, column);
863         if (expectedValue == null) {
864             assertNull("Column value " + column, value);
865         } else {
866             assertEquals("Column value " + column, String.valueOf(expectedValue), value);
867         }
868     }
869 
assertStoredValue(Uri rowUri, String selection, String[] selectionArgs, String column, Object expectedValue)870     protected void assertStoredValue(Uri rowUri, String selection, String[] selectionArgs,
871             String column, Object expectedValue) {
872         String value = getStoredValue(rowUri, selection, selectionArgs, column);
873         if (expectedValue == null) {
874             assertNull("Column value " + column, value);
875         } else {
876             assertEquals("Column value " + column, String.valueOf(expectedValue), value);
877         }
878     }
879 
getStoredValue(Uri rowUri, String column)880     protected String getStoredValue(Uri rowUri, String column) {
881         return getStoredValue(rowUri, null, null, column);
882     }
883 
getStoredValue(Uri uri, String selection, String[] selectionArgs, String column)884     protected String getStoredValue(Uri uri, String selection, String[] selectionArgs,
885             String column) {
886         String value = null;
887         Cursor c = mResolver.query(uri, new String[] { column }, selection, selectionArgs, null);
888         try {
889             assertEquals("Record count for " + uri, 1, c.getCount());
890 
891             if (c.moveToFirst()) {
892                 value = c.getString(c.getColumnIndex(column));
893             }
894         } finally {
895             c.close();
896         }
897         return value;
898     }
899 
getStoredLongValue(Uri uri, String selection, String[] selectionArgs, String column)900     protected Long getStoredLongValue(Uri uri, String selection, String[] selectionArgs,
901             String column) {
902         Long value = null;
903         Cursor c = mResolver.query(uri, new String[] { column }, selection, selectionArgs, null);
904         try {
905             assertEquals("Record count", 1, c.getCount());
906 
907             if (c.moveToFirst()) {
908                 value = c.getLong(c.getColumnIndex(column));
909             }
910         } finally {
911             c.close();
912         }
913         return value;
914     }
915 
getStoredLongValue(Uri uri, String column)916     protected Long getStoredLongValue(Uri uri, String column) {
917         return getStoredLongValue(uri, null, null, column);
918     }
919 
assertStoredValues(Uri rowUri, ContentValues expectedValues)920     protected void assertStoredValues(Uri rowUri, ContentValues expectedValues) {
921         assertStoredValues(rowUri, null, null, expectedValues);
922     }
923 
assertStoredValues(Uri rowUri, ContentValues... expectedValues)924     protected void assertStoredValues(Uri rowUri, ContentValues... expectedValues) {
925         assertStoredValues(rowUri, null, null, expectedValues);
926     }
927 
assertStoredValues(Uri rowUri, String selection, String[] selectionArgs, ContentValues expectedValues)928     protected void assertStoredValues(Uri rowUri, String selection, String[] selectionArgs,
929             ContentValues expectedValues) {
930         Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null);
931         try {
932             assertEquals("Record count", 1, c.getCount());
933             c.moveToFirst();
934             assertCursorValues(c, expectedValues);
935         } catch (Error e) {
936             TestUtils.dumpCursor(c);
937             throw e;
938         } finally {
939             c.close();
940         }
941     }
942 
assertContainsValues(Uri rowUri, ContentValues expectedValues)943     protected void assertContainsValues(Uri rowUri, ContentValues expectedValues) {
944         Cursor c = mResolver.query(rowUri, null, null, null, null);
945         try {
946             assertEquals("Record count", 1, c.getCount());
947             c.moveToFirst();
948             assertCursorValuesPartialMatch(c, expectedValues);
949         } catch (Error e) {
950             TestUtils.dumpCursor(c);
951             throw e;
952         } finally {
953             c.close();
954         }
955     }
956 
assertStoredValuesWithProjection(Uri rowUri, ContentValues expectedValues)957     protected void assertStoredValuesWithProjection(Uri rowUri, ContentValues expectedValues) {
958         assertStoredValuesWithProjection(rowUri, new ContentValues[] {expectedValues});
959     }
960 
assertStoredValuesWithProjection(Uri rowUri, ContentValues... expectedValues)961     protected void assertStoredValuesWithProjection(Uri rowUri, ContentValues... expectedValues) {
962         assertTrue("Need at least one ContentValues for this test", expectedValues.length > 0);
963         Cursor c = mResolver.query(rowUri, buildProjection(expectedValues[0]), null, null, null);
964         try {
965             assertEquals("Record count", expectedValues.length, c.getCount());
966             c.moveToFirst();
967             assertCursorValues(c, expectedValues);
968         } catch (Error e) {
969             TestUtils.dumpCursor(c);
970             throw e;
971         } finally {
972             c.close();
973         }
974     }
975 
assertStoredValues( Uri rowUri, String selection, String[] selectionArgs, ContentValues... expectedValues)976     protected void assertStoredValues(
977             Uri rowUri, String selection, String[] selectionArgs, ContentValues... expectedValues) {
978         assertStoredValues(mResolver.query(rowUri, null, selection, selectionArgs, null),
979                 expectedValues);
980     }
981 
assertStoredValues(Cursor c, ContentValues... expectedValues)982     private void assertStoredValues(Cursor c, ContentValues... expectedValues) {
983         try {
984             assertEquals("Record count", expectedValues.length, c.getCount());
985             assertCursorValues(c, expectedValues);
986         } catch (Error e) {
987             TestUtils.dumpCursor(c);
988             throw e;
989         } finally {
990             c.close();
991         }
992     }
993 
994     /**
995      * A variation of {@link #assertStoredValues}, but it queries directly to the DB.
996      */
assertStoredValuesDb( String sql, String[] selectionArgs, ContentValues... expectedValues)997     protected void assertStoredValuesDb(
998             String sql, String[] selectionArgs, ContentValues... expectedValues) {
999         SQLiteDatabase db = ((ContactsProvider2) getProvider()).getDatabaseHelper()
1000                 .getReadableDatabase();
1001         assertStoredValues(db.rawQuery(sql, selectionArgs), expectedValues);
1002     }
1003 
assertStoredValuesOrderly(Uri rowUri, ContentValues... expectedValues)1004     protected void assertStoredValuesOrderly(Uri rowUri, ContentValues... expectedValues) {
1005         assertStoredValuesOrderly(rowUri, null, null, expectedValues);
1006     }
1007 
assertStoredValuesOrderly(Uri rowUri, String selection, String[] selectionArgs, ContentValues... expectedValues)1008     protected void assertStoredValuesOrderly(Uri rowUri, String selection,
1009             String[] selectionArgs, ContentValues... expectedValues) {
1010         Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null);
1011         try {
1012             assertEquals("Record count", expectedValues.length, c.getCount());
1013             assertCursorValuesOrderly(c, expectedValues);
1014         } catch (Error e) {
1015             TestUtils.dumpCursor(c);
1016             throw e;
1017         } finally {
1018             c.close();
1019         }
1020     }
1021 
1022     /**
1023      * Constructs a selection (where clause) out of all supplied values, uses it
1024      * to query the provider and verifies that a single row is returned and it
1025      * has the same values as requested.
1026      */
assertSelection(Uri uri, ContentValues values, String idColumn, long id)1027     protected void assertSelection(Uri uri, ContentValues values, String idColumn, long id) {
1028         assertSelection(uri, values, idColumn, id, null);
1029     }
1030 
assertSelectionWithProjection(Uri uri, ContentValues values, String idColumn, long id)1031     public void assertSelectionWithProjection(Uri uri, ContentValues values, String idColumn,
1032             long id) {
1033         assertSelection(uri, values, idColumn, id, buildProjection(values));
1034     }
1035 
assertSelection(Uri uri, ContentValues values, String idColumn, long id, String[] projection)1036     private void assertSelection(Uri uri, ContentValues values, String idColumn, long id,
1037             String[] projection) {
1038         StringBuilder sb = new StringBuilder();
1039         ArrayList<String> selectionArgs = new ArrayList<String>(values.size());
1040         if (idColumn != null) {
1041             sb.append(idColumn).append("=").append(id);
1042         }
1043         Set<Map.Entry<String, Object>> entries = values.valueSet();
1044         for (Map.Entry<String, Object> entry : entries) {
1045             String column = entry.getKey();
1046             Object value = entry.getValue();
1047             if (sb.length() != 0) {
1048                 sb.append(" AND ");
1049             }
1050             sb.append(column);
1051             if (value == null) {
1052                 sb.append(" IS NULL");
1053             } else {
1054                 sb.append("=?");
1055                 selectionArgs.add(String.valueOf(value));
1056             }
1057         }
1058 
1059         Cursor c = mResolver.query(uri, projection, sb.toString(), selectionArgs.toArray(new String[0]),
1060                 null);
1061         try {
1062             assertEquals("Record count", 1, c.getCount());
1063             c.moveToFirst();
1064             assertCursorValues(c, values);
1065         } catch (Error e) {
1066             TestUtils.dumpCursor(c);
1067             throw e;
1068         } finally {
1069             c.close();
1070         }
1071     }
1072 
assertCursorValue(Cursor cursor, String column, Object expectedValue)1073     protected void assertCursorValue(Cursor cursor, String column, Object expectedValue) {
1074         String actualValue = cursor.getString(cursor.getColumnIndex(column));
1075         assertEquals("Column " + column, String.valueOf(expectedValue),
1076                 String.valueOf(actualValue));
1077     }
1078 
assertCursorValues(Cursor cursor, ContentValues expectedValues)1079     protected void assertCursorValues(Cursor cursor, ContentValues expectedValues) {
1080         StringBuilder message = new StringBuilder();
1081         boolean result = equalsWithExpectedValues(cursor, expectedValues, message);
1082         assertTrue(message.toString(), result);
1083     }
1084 
assertCursorValuesPartialMatch(Cursor cursor, ContentValues expectedValues)1085     protected void assertCursorValuesPartialMatch(Cursor cursor, ContentValues expectedValues) {
1086         StringBuilder message = new StringBuilder();
1087         boolean result = expectedValuePartiallyMatches(cursor, expectedValues, message);
1088         assertTrue(message.toString(), result);
1089     }
1090 
assertCursorHasAnyRecordMatch(Cursor cursor, ContentValues expectedValues)1091     protected void assertCursorHasAnyRecordMatch(Cursor cursor, ContentValues expectedValues) {
1092         final StringBuilder message = new StringBuilder();
1093         boolean found = false;
1094         cursor.moveToPosition(-1);
1095         while (cursor.moveToNext()) {
1096             message.setLength(0);
1097             final int pos = cursor.getPosition();
1098             found = equalsWithExpectedValues(cursor, expectedValues, message);
1099             if (found) {
1100                 break;
1101             }
1102         }
1103         assertTrue("Expected values can not be found " + expectedValues + "," + message.toString(),
1104                 found);
1105     }
1106 
assertCursorValues(Cursor cursor, ContentValues... expectedValues)1107     protected void assertCursorValues(Cursor cursor, ContentValues... expectedValues) {
1108         StringBuilder message = new StringBuilder();
1109 
1110         // In case if expectedValues contains multiple identical values, remember which cursor
1111         // rows are "consumed" to prevent multiple ContentValues from hitting the same row.
1112         final BitSet used = new BitSet(cursor.getCount());
1113 
1114         for (ContentValues v : expectedValues) {
1115             boolean found = false;
1116             cursor.moveToPosition(-1);
1117             while (cursor.moveToNext()) {
1118                 final int pos = cursor.getPosition();
1119                 if (used.get(pos)) continue;
1120                 found = equalsWithExpectedValues(cursor, v, message);
1121                 if (found) {
1122                     used.set(pos);
1123                     break;
1124                 }
1125             }
1126             assertTrue("Expected values can not be found " + v + "," + message.toString(), found);
1127         }
1128     }
1129 
assertCursorValuesOrderly(Cursor cursor, ContentValues... expectedValues)1130     private void assertCursorValuesOrderly(Cursor cursor, ContentValues... expectedValues) {
1131         StringBuilder message = new StringBuilder();
1132         cursor.moveToPosition(-1);
1133         for (ContentValues v : expectedValues) {
1134             assertTrue(cursor.moveToNext());
1135             boolean ok = equalsWithExpectedValues(cursor, v, message);
1136             assertTrue("ContentValues didn't match.  Pos=" + cursor.getPosition() + ", values=" +
1137                     v + message.toString(), ok);
1138         }
1139     }
1140 
expectedValuePartiallyMatches(Cursor cursor, ContentValues expectedValues, StringBuilder msgBuffer)1141     private boolean expectedValuePartiallyMatches(Cursor cursor, ContentValues expectedValues,
1142             StringBuilder msgBuffer) {
1143         for (String column : expectedValues.keySet()) {
1144             int index = cursor.getColumnIndex(column);
1145             if (index == -1) {
1146                 msgBuffer.append(" No such column: ").append(column);
1147                 return false;
1148             }
1149             String expectedValue = expectedValues.getAsString(column);
1150             String value = cursor.getString(cursor.getColumnIndex(column));
1151             if (value != null && !value.contains(expectedValue)) {
1152                 msgBuffer.append(" Column value ").append(column).append(" expected to contain <")
1153                         .append(expectedValue).append(">, but was <").append(value).append('>');
1154                 return false;
1155             }
1156         }
1157         return true;
1158     }
1159 
equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues, StringBuilder msgBuffer)1160     private boolean equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues,
1161             StringBuilder msgBuffer) {
1162         for (String column : expectedValues.keySet()) {
1163             int index = cursor.getColumnIndex(column);
1164             if (index == -1) {
1165                 msgBuffer.append(" No such column: ").append(column);
1166                 return false;
1167             }
1168             Object expectedValue = expectedValues.get(column);
1169             String value;
1170             if (expectedValue instanceof byte[]) {
1171                 expectedValue = Hex.encodeHex((byte[])expectedValue, false);
1172                 value = Hex.encodeHex(cursor.getBlob(index), false);
1173             } else {
1174                 expectedValue = expectedValues.getAsString(column);
1175                 value = cursor.getString(cursor.getColumnIndex(column));
1176             }
1177             if (expectedValue != null && !expectedValue.equals(value) || value != null
1178                     && !value.equals(expectedValue)) {
1179                 msgBuffer
1180                         .append(" Column value ")
1181                         .append(column)
1182                         .append(" expected <")
1183                         .append(expectedValue)
1184                         .append(">, but was <")
1185                         .append(value)
1186                         .append('>');
1187                 return false;
1188             }
1189         }
1190         return true;
1191     }
1192 
1193     private static final String[] DATA_USAGE_PROJECTION =
1194             new String[] {Data.DATA1, Data.TIMES_USED, Data.LAST_TIME_USED};
1195 
assertDataUsageCursorContains(Uri uri, String data1, int timesUsed, int lastTimeUsed)1196     protected void assertDataUsageCursorContains(Uri uri, String data1, int timesUsed,
1197             int lastTimeUsed) {
1198         final Cursor cursor = mResolver.query(uri, DATA_USAGE_PROJECTION, null, null,
1199                 null);
1200         try {
1201             assertCursorHasAnyRecordMatch(cursor, cv(Data.DATA1, data1, Data.TIMES_USED, timesUsed,
1202                     Data.LAST_TIME_USED, lastTimeUsed));
1203         } finally {
1204             cursor.close();
1205         }
1206     }
1207 
buildProjection(ContentValues values)1208     private String[] buildProjection(ContentValues values) {
1209         String[] projection = new String[values.size()];
1210         Iterator<Entry<String, Object>> iter = values.valueSet().iterator();
1211         for (int i = 0; i < projection.length; i++) {
1212             projection[i] = iter.next().getKey();
1213         }
1214         return projection;
1215     }
1216 
getCount(Uri uri)1217     protected int getCount(Uri uri) {
1218         return getCount(uri, null, null);
1219     }
1220 
getCount(Uri uri, String selection, String[] selectionArgs)1221     protected int getCount(Uri uri, String selection, String[] selectionArgs) {
1222         Cursor c = mResolver.query(uri, null, selection, selectionArgs, null);
1223         try {
1224             return c.getCount();
1225         } finally {
1226             c.close();
1227         }
1228     }
1229 
dump(ContentResolver resolver, boolean aggregatedOnly)1230     public static void dump(ContentResolver resolver, boolean aggregatedOnly) {
1231         String[] projection = new String[] {
1232                 Contacts._ID,
1233                 Contacts.DISPLAY_NAME
1234         };
1235         String selection = null;
1236         if (aggregatedOnly) {
1237             selection = Contacts._ID
1238                     + " IN (SELECT contact_id" +
1239                     		" FROM raw_contacts GROUP BY contact_id HAVING count(*) > 1)";
1240         }
1241 
1242         Cursor c = resolver.query(Contacts.CONTENT_URI, projection, selection, null,
1243                 Contacts.DISPLAY_NAME);
1244         while(c.moveToNext()) {
1245             long contactId = c.getLong(0);
1246             Log.i("Contact   ", String.format("%5d %s", contactId, c.getString(1)));
1247             dumpRawContacts(resolver, contactId);
1248             Log.i("          ", ".");
1249         }
1250         c.close();
1251     }
1252 
dumpRawContacts(ContentResolver resolver, long contactId)1253     private static void dumpRawContacts(ContentResolver resolver, long contactId) {
1254         String[] projection = new String[] {
1255                 RawContacts._ID,
1256         };
1257         Cursor c = resolver.query(RawContacts.CONTENT_URI, projection, RawContacts.CONTACT_ID + "="
1258                 + contactId, null, null);
1259         while(c.moveToNext()) {
1260             long rawContactId = c.getLong(0);
1261             Log.i("RawContact", String.format("      %-5d", rawContactId));
1262             dumpData(resolver, rawContactId);
1263         }
1264         c.close();
1265     }
1266 
dumpData(ContentResolver resolver, long rawContactId)1267     private static void dumpData(ContentResolver resolver, long rawContactId) {
1268         String[] projection = new String[] {
1269                 Data.MIMETYPE,
1270                 Data.DATA1,
1271                 Data.DATA2,
1272                 Data.DATA3,
1273         };
1274         Cursor c = resolver.query(Data.CONTENT_URI, projection, Data.RAW_CONTACT_ID + "="
1275                 + rawContactId, null, Data.MIMETYPE);
1276         while(c.moveToNext()) {
1277             String mimetype = c.getString(0);
1278             if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
1279                 Log.i("Photo     ", "");
1280             } else {
1281                 mimetype = mimetype.substring(mimetype.indexOf('/') + 1);
1282                 Log.i("Data      ", String.format("            %-10s %s,%s,%s", mimetype,
1283                         c.getString(1), c.getString(2), c.getString(3)));
1284             }
1285         }
1286         c.close();
1287     }
1288 
assertNetworkNotified(boolean expected)1289     protected void assertNetworkNotified(boolean expected) {
1290         assertEquals(expected, (getContactsProvider()).isNetworkNotified());
1291     }
1292 
assertProjection(Uri uri, String[] expectedProjection)1293     protected void assertProjection(Uri uri, String[] expectedProjection) {
1294         Cursor cursor = mResolver.query(uri, null, "0", null, null);
1295         String[] actualProjection = cursor.getColumnNames();
1296         MoreAsserts.assertEquals("Incorrect projection for URI: " + uri,
1297                 Sets.newHashSet(expectedProjection), Sets.newHashSet(actualProjection));
1298         cursor.close();
1299     }
1300 
assertRowCount(int expectedCount, Uri uri, String selection, String[] args)1301     protected void assertRowCount(int expectedCount, Uri uri, String selection, String[] args) {
1302         Cursor cursor = mResolver.query(uri, null, selection, args, null);
1303 
1304         try {
1305             assertEquals(expectedCount, cursor.getCount());
1306         } catch (Error e) {
1307             TestUtils.dumpCursor(cursor);
1308             throw e;
1309         } finally {
1310             cursor.close();
1311         }
1312     }
1313 
1314     /**
1315      * A contact in the database, and the attributes used to create it.  Construct using
1316      * {@link GoldenContactBuilder#build()}.
1317      */
1318     public final class GoldenContact {
1319 
1320         private final long rawContactId;
1321 
1322         private final long contactId;
1323 
1324         private final String givenName;
1325 
1326         private final String familyName;
1327 
1328         private final String nickname;
1329 
1330         private final byte[] photo;
1331 
1332         private final String company;
1333 
1334         private final String title;
1335 
1336         private final String phone;
1337 
1338         private final String email;
1339 
GoldenContact(GoldenContactBuilder builder, long rawContactId, long contactId)1340         private GoldenContact(GoldenContactBuilder builder, long rawContactId, long contactId) {
1341 
1342             this.rawContactId = rawContactId;
1343             this.contactId = contactId;
1344             givenName = builder.givenName;
1345             familyName = builder.familyName;
1346             nickname = builder.nickname;
1347             photo = builder.photo;
1348             company = builder.company;
1349             title = builder.title;
1350             phone = builder.phone;
1351             email = builder.email;
1352         }
1353 
delete()1354         public void delete() {
1355             Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1356             mResolver.delete(rawContactUri, null, null);
1357         }
1358 
1359         /**
1360          * Returns the index of the contact in table "raw_contacts"
1361          */
getRawContactId()1362         public long getRawContactId() {
1363             return rawContactId;
1364         }
1365 
1366         /**
1367          * Returns the index of the contact in table "contacts"
1368          */
getContactId()1369         public long getContactId() {
1370             return contactId;
1371         }
1372 
1373         /**
1374          * Returns the lookup key for the contact.
1375          */
getLookupKey()1376         public String getLookupKey() {
1377             return queryLookupKey(contactId);
1378         }
1379 
1380         /**
1381          * Returns the contact's given name.
1382          */
getGivenName()1383         public String getGivenName() {
1384             return givenName;
1385         }
1386 
1387         /**
1388          * Returns the contact's family name.
1389          */
getFamilyName()1390         public String getFamilyName() {
1391             return familyName;
1392         }
1393 
1394         /**
1395          * Returns the contact's nickname.
1396          */
getNickname()1397         public String getNickname() {
1398             return nickname;
1399         }
1400 
1401         /**
1402          * Return's the contact's photo
1403          */
getPhoto()1404         public byte[] getPhoto() {
1405             return photo;
1406         }
1407 
1408         /**
1409          * Return's the company at which the contact works.
1410          */
getCompany()1411         public String getCompany() {
1412             return company;
1413         }
1414 
1415         /**
1416          * Returns the contact's job title.
1417          */
getTitle()1418         public String getTitle() {
1419             return title;
1420         }
1421 
1422         /**
1423          * Returns the contact's phone number
1424          */
getPhone()1425         public String getPhone() {
1426             return phone;
1427         }
1428 
1429         /**
1430          * Returns the contact's email address
1431          */
getEmail()1432         public String getEmail() {
1433             return email;
1434         }
1435      }
1436 
1437     /**
1438      * Builds {@link GoldenContact} objects.  Unspecified boolean objects default to false.
1439      * Unspecified String objects default to null.
1440      */
1441     public final class GoldenContactBuilder {
1442 
1443         private String givenName;
1444 
1445         private String familyName;
1446 
1447         private String nickname;
1448 
1449         private byte[] photo;
1450 
1451         private String company;
1452 
1453         private String title;
1454 
1455         private String phone;
1456 
1457         private String email;
1458 
1459         /**
1460          * The contact's given and family names.
1461          *
1462          * TODO(dplotnikov): inline, or should we require them to set both names if they set either?
1463          */
name(String givenName, String familyName)1464         public GoldenContactBuilder name(String givenName, String familyName) {
1465             return givenName(givenName).familyName(familyName);
1466         }
1467 
1468         /**
1469          * The contact's given name.
1470          */
givenName(String value)1471         public GoldenContactBuilder givenName(String value) {
1472             givenName = value;
1473             return this;
1474         }
1475 
1476         /**
1477          * The contact's family name.
1478          */
familyName(String value)1479         public GoldenContactBuilder familyName(String value) {
1480             familyName = value;
1481             return this;
1482         }
1483 
1484         /**
1485          * The contact's nickname.
1486          */
nickname(String value)1487         public GoldenContactBuilder nickname(String value) {
1488             nickname = value;
1489             return this;
1490         }
1491 
1492         /**
1493          * The contact's photo.
1494          */
photo(byte[] value)1495         public GoldenContactBuilder photo(byte[] value) {
1496             photo = value;
1497             return this;
1498         }
1499 
1500         /**
1501          * The company at which the contact works.
1502          */
company(String value)1503         public GoldenContactBuilder company(String value) {
1504             company = value;
1505             return this;
1506         }
1507 
1508         /**
1509          * The contact's job title.
1510          */
title(String value)1511         public GoldenContactBuilder title(String value) {
1512             title = value;
1513             return this;
1514         }
1515 
1516         /**
1517          * The contact's phone number.
1518          */
phone(String value)1519         public GoldenContactBuilder phone(String value) {
1520             phone = value;
1521             return this;
1522         }
1523 
1524         /**
1525          * The contact's email address; also sets their IM status to {@link StatusUpdates#OFFLINE}
1526          * with a presence of "Coding for Android".
1527          */
email(String value)1528         public GoldenContactBuilder email(String value) {
1529             email = value;
1530             return this;
1531         }
1532 
1533         /**
1534          * Builds the {@link GoldenContact} specified by this builder.
1535          */
build()1536         public GoldenContact build() {
1537 
1538             final long groupId = createGroup(mAccount, "gsid1", "title1");
1539 
1540             long rawContactId = RawContactUtil.createRawContact(mResolver);
1541             insertGroupMembership(rawContactId, groupId);
1542 
1543             if (givenName != null || familyName != null) {
1544                 DataUtil.insertStructuredName(mResolver, rawContactId, givenName, familyName);
1545             }
1546             if (nickname != null) {
1547                 insertNickname(rawContactId, nickname);
1548             }
1549             if (photo != null) {
1550                 insertPhoto(rawContactId);
1551             }
1552             if (company != null || title != null) {
1553                 insertOrganization(rawContactId);
1554             }
1555             if (email != null) {
1556                 insertEmail(rawContactId);
1557             }
1558             if (phone != null) {
1559                 insertPhone(rawContactId);
1560             }
1561 
1562             long contactId = queryContactId(rawContactId);
1563 
1564             return new GoldenContact(this, rawContactId, contactId);
1565         }
1566 
insertPhoto(long rawContactId)1567         private void insertPhoto(long rawContactId) {
1568             ContentValues values = new ContentValues();
1569             values.put(Data.RAW_CONTACT_ID, rawContactId);
1570             values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
1571             values.put(Photo.PHOTO, photo);
1572             mResolver.insert(Data.CONTENT_URI, values);
1573         }
1574 
insertOrganization(long rawContactId)1575         private void insertOrganization(long rawContactId) {
1576 
1577             ContentValues values = new ContentValues();
1578             values.put(Data.RAW_CONTACT_ID, rawContactId);
1579             values.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
1580             values.put(Organization.TYPE, Organization.TYPE_WORK);
1581             if (company != null) {
1582                 values.put(Organization.COMPANY, company);
1583             }
1584             if (title != null) {
1585                 values.put(Organization.TITLE, title);
1586             }
1587             mResolver.insert(Data.CONTENT_URI, values);
1588         }
1589 
insertEmail(long rawContactId)1590         private void insertEmail(long rawContactId) {
1591 
1592             ContentValues values = new ContentValues();
1593             values.put(Data.RAW_CONTACT_ID, rawContactId);
1594             values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
1595             values.put(Email.TYPE, Email.TYPE_WORK);
1596             values.put(Email.DATA, "foo@acme.com");
1597             mResolver.insert(Data.CONTENT_URI, values);
1598 
1599             int protocol = Im.PROTOCOL_GOOGLE_TALK;
1600 
1601             values.clear();
1602             values.put(StatusUpdates.PROTOCOL, protocol);
1603             values.put(StatusUpdates.IM_HANDLE, email);
1604             values.put(StatusUpdates.IM_ACCOUNT, "foo");
1605             values.put(StatusUpdates.PRESENCE_STATUS, StatusUpdates.OFFLINE);
1606             values.put(StatusUpdates.CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
1607             values.put(StatusUpdates.PRESENCE_CUSTOM_STATUS, "Coding for Android");
1608             mResolver.insert(StatusUpdates.CONTENT_URI, values);
1609         }
1610 
insertPhone(long rawContactId)1611         private void insertPhone(long rawContactId) {
1612             ContentValues values = new ContentValues();
1613             values.put(Data.RAW_CONTACT_ID, rawContactId);
1614             values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1615             values.put(Data.IS_PRIMARY, 1);
1616             values.put(Phone.TYPE, Phone.TYPE_HOME);
1617             values.put(Phone.NUMBER, phone);
1618             mResolver.insert(Data.CONTENT_URI, values);
1619         }
1620     }
1621 }
1622