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