• 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.TestUtils.cv;
20 import static com.android.providers.contacts.TestUtils.dumpCursor;
21 
22 import static org.mockito.Mockito.eq;
23 import static org.mockito.Mockito.when;
24 
25 import android.accounts.Account;
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.ContentProviderOperation;
29 import android.content.ContentProviderResult;
30 import android.content.ContentResolver;
31 import android.content.ContentUris;
32 import android.content.ContentValues;
33 import android.content.Entity;
34 import android.content.EntityIterator;
35 import android.content.Intent;
36 import android.content.res.AssetFileDescriptor;
37 import android.database.Cursor;
38 import android.database.DatabaseUtils;
39 import android.database.MatrixCursor;
40 import android.database.sqlite.SQLiteDatabase;
41 import android.net.Uri;
42 import android.os.AsyncTask;
43 import android.os.Bundle;
44 import android.provider.ContactsContract;
45 import android.provider.ContactsContract.AggregationExceptions;
46 import android.provider.ContactsContract.CommonDataKinds.Callable;
47 import android.provider.ContactsContract.CommonDataKinds.Contactables;
48 import android.provider.ContactsContract.CommonDataKinds.Email;
49 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
50 import android.provider.ContactsContract.CommonDataKinds.Im;
51 import android.provider.ContactsContract.CommonDataKinds.Organization;
52 import android.provider.ContactsContract.CommonDataKinds.Phone;
53 import android.provider.ContactsContract.CommonDataKinds.Photo;
54 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
55 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
56 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
57 import android.provider.ContactsContract.Contacts;
58 import android.provider.ContactsContract.Data;
59 import android.provider.ContactsContract.DataUsageFeedback;
60 import android.provider.ContactsContract.Directory;
61 import android.provider.ContactsContract.DisplayNameSources;
62 import android.provider.ContactsContract.DisplayPhoto;
63 import android.provider.ContactsContract.FullNameStyle;
64 import android.provider.ContactsContract.Groups;
65 import android.provider.ContactsContract.PhoneLookup;
66 import android.provider.ContactsContract.PhoneticNameStyle;
67 import android.provider.ContactsContract.PinnedPositions;
68 import android.provider.ContactsContract.Profile;
69 import android.provider.ContactsContract.ProviderStatus;
70 import android.provider.ContactsContract.RawContacts;
71 import android.provider.ContactsContract.RawContactsEntity;
72 import android.provider.ContactsContract.SearchSnippets;
73 import android.provider.ContactsContract.Settings;
74 import android.provider.ContactsContract.StatusUpdates;
75 import android.provider.ContactsContract.StreamItemPhotos;
76 import android.provider.ContactsContract.StreamItems;
77 import android.provider.OpenableColumns;
78 import android.telecom.PhoneAccountHandle;
79 import android.telecom.TelecomManager;
80 import android.telephony.PhoneNumberUtils;
81 import android.telephony.SubscriptionInfo;
82 import android.test.MoreAsserts;
83 import android.test.suitebuilder.annotation.LargeTest;
84 import android.text.TextUtils;
85 import android.util.ArraySet;
86 
87 import com.android.internal.util.ArrayUtils;
88 import com.android.providers.contacts.ContactsActor.AlteringUserContext;
89 import com.android.providers.contacts.ContactsActor.MockUserManager;
90 import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
91 import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
92 import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
93 import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
94 import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
95 import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
96 import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
97 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
98 import com.android.providers.contacts.tests.R;
99 import com.android.providers.contacts.testutil.CommonDatabaseUtils;
100 import com.android.providers.contacts.testutil.ContactUtil;
101 import com.android.providers.contacts.testutil.DataUtil;
102 import com.android.providers.contacts.testutil.DatabaseAsserts;
103 import com.android.providers.contacts.testutil.DeletedContactUtil;
104 import com.android.providers.contacts.testutil.RawContactUtil;
105 import com.android.providers.contacts.testutil.TestUtil;
106 import com.android.providers.contacts.util.NullContentProvider;
107 import com.android.providers.contacts.util.PhoneAccountHandleMigrationUtils;
108 import com.android.providers.contacts.util.UserUtils;
109 
110 import com.google.android.collect.Lists;
111 import com.google.android.collect.Sets;
112 
113 import java.io.FileInputStream;
114 import java.io.IOException;
115 import java.io.OutputStream;
116 import java.text.Collator;
117 import java.util.ArrayList;
118 import java.util.Arrays;
119 import java.util.HashSet;
120 import java.util.List;
121 import java.util.Locale;
122 import java.util.Set;
123 
124 /**
125  * Unit tests for {@link ContactsProvider2}.
126  *
127  * Run the test like this:
128  * <code>
129    adb shell am instrument -e class com.android.providers.contacts.ContactsProvider2Test -w \
130            com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
131  * </code>
132  */
133 @LargeTest
134 public class ContactsProvider2Test extends BaseContactsProvider2Test {
135 
136     private static final String TAG = ContactsProvider2Test.class.getSimpleName();
137 
138     private static final int MIN_MATCH = 7;
139 
140     static final String TELEPHONY_PACKAGE = "com.android.phone";
141     static final String TELEPHONY_CLASS
142             = "com.android.services.telephony.TelephonyConnectionService";
143     static final String TEST_PHONE_ACCOUNT_HANDLE_SUB_ID = "666";
144     static final int TEST_PHONE_ACCOUNT_HANDLE_SUB_ID_INT = 666;
145     static final String TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1 = "T6E6S6T6I6C6C6I6D";
146     static final String TEST_PHONE_ACCOUNT_HANDLE_ICC_ID2 = "T5E5S5T5I5C5C5I5D";
147     static final String TEST_COMPONENT_NAME = "foo/bar";
148 
149     private int mOldMinMatch1;
150     private int mOldMinMatch2;
151 
152     ContactsDatabaseHelper mMockContactsDatabaseHelper;
153     private ContactsProvider2 mContactsProvider2;
154     private ContactsDatabaseHelper mDbHelper;
155     private BroadcastReceiver mBroadcastReceiver;
156 
157     @Override
setUp()158     protected void setUp() throws Exception {
159         super.setUp();
160         mContactsProvider2 = (ContactsProvider2) getProvider();
161         mDbHelper = mContactsProvider2.getThreadActiveDatabaseHelperForTest();
162         mBroadcastReceiver = mContactsProvider2.getBroadcastReceiverForTest();
163         mOldMinMatch1 = PhoneNumberUtils.getMinMatchForTest();
164         mOldMinMatch2 = mDbHelper.getMinMatchForTest();
165         PhoneNumberUtils.setMinMatchForTest(MIN_MATCH);
166         mDbHelper.setMinMatchForTest(MIN_MATCH);
167     }
168 
169     @Override
tearDown()170     protected void tearDown() throws Exception {
171         final ContactsProvider2 cp = (ContactsProvider2) getProvider();
172         //final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
173         PhoneNumberUtils.setMinMatchForTest(mOldMinMatch1);
174         mDbHelper.setMinMatchForTest(mOldMinMatch2);
175         super.tearDown();
176     }
177 
getMockContactsDatabaseHelper(String databaseNameForTesting)178     private ContactsDatabaseHelper getMockContactsDatabaseHelper(String databaseNameForTesting) {
179         ContactsDatabaseHelper contactsDatabaseHelper = new ContactsDatabaseHelper(
180                 mTestContext, databaseNameForTesting, true, /* isTestInstance=*/ false);
181         SQLiteDatabase db = contactsDatabaseHelper.getWritableDatabase();
182         db.execSQL("DELETE FROM " + ContactsDatabaseHelper.Tables.DATA);
183         {
184             final ContentValues values = new ContentValues();
185             values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666);
186             values.put(Data.RAW_CONTACT_ID, 6666);
187             values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
188                     PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME);
189             values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1);
190             values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1);
191             long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values);
192         }
193         {
194             final ContentValues values = new ContentValues();
195             values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666);
196             values.put(Data.RAW_CONTACT_ID, 6666);
197             values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
198                     PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME);
199             values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1);
200             values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1);
201             long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values);
202         }
203         {
204             final ContentValues values = new ContentValues();
205             values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666);
206             values.put(Data.RAW_CONTACT_ID, 6666);
207             values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
208                     PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME);
209             values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1);
210             values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1);
211             long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values);
212         }
213         {
214             final ContentValues values = new ContentValues();
215             values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666);
216             values.put(Data.RAW_CONTACT_ID, 6666);
217             values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
218                     PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME);
219             values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1);
220             values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1);
221             long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values);
222         }
223         {
224             final ContentValues values = new ContentValues();
225             values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666);
226             values.put(Data.RAW_CONTACT_ID, 6666);
227             values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
228                     PhoneAccountHandleMigrationUtils.TELEPHONY_COMPONENT_NAME);
229             values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1);
230             values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "FAKE_ICCID");
231             long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values);
232         }
233         {
234             final ContentValues values = new ContentValues();
235             values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, 6666);
236             values.put(Data.RAW_CONTACT_ID, 6666);
237             values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, TEST_COMPONENT_NAME);
238             values.put(Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING, 1);
239             values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "FAKE_ICCID");
240             long count = db.insert(ContactsDatabaseHelper.Tables.DATA, null, values);
241         }
242         return contactsDatabaseHelper;
243     }
244 
testPhoneAccountHandleMigrationSimEvent()245     public void testPhoneAccountHandleMigrationSimEvent() throws IOException {
246         ContactsDatabaseHelper originalContactsDatabaseHelper
247                 = mContactsProvider2.getContactsDatabaseHelperForTest();
248 
249         // Mock SubscriptionManager
250         SubscriptionInfo subscriptionInfo = new SubscriptionInfo(
251                 TEST_PHONE_ACCOUNT_HANDLE_SUB_ID_INT, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1,
252                         1, "a", "b", 1, 1, "test", 1, null, null, null, null, false, null, null);
253         when(mSubscriptionManager.getActiveSubscriptionInfo(
254                 eq(TEST_PHONE_ACCOUNT_HANDLE_SUB_ID_INT))).thenReturn(subscriptionInfo);
255 
256         // Mock ContactsDatabaseHelper
257         ContactsDatabaseHelper contactsDatabaseHelper = getMockContactsDatabaseHelper(
258                 "testContactsPhoneAccountHandleMigrationSimEvent.db");
259 
260         // Test setPhoneAccountMigrationStatusPending as false
261         PhoneAccountHandleMigrationUtils phoneAccountHandleMigrationUtils
262                 = contactsDatabaseHelper.getPhoneAccountHandleMigrationUtils();
263 
264         // Test ContactsDatabaseHelper.isPhoneAccountMigrationPending as true
265         // and set for testing migration logic
266         phoneAccountHandleMigrationUtils.setPhoneAccountMigrationStatusPending(true);
267 
268         mContactsProvider2.setContactsDatabaseHelperForTest(contactsDatabaseHelper);
269         final SQLiteDatabase sqLiteDatabase = contactsDatabaseHelper.getReadableDatabase();
270 
271         // Check each entry in the Data has a new coloumn of
272         // Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING that has a value of 1
273         assertEquals(6 /** pending migration entries in the preconfigured file */,
274                 DatabaseUtils.longForQuery(sqLiteDatabase,
275                         "select count(*) from " + ContactsDatabaseHelper.Tables.DATA
276                                 + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING
277                                         + " = 1", null));
278 
279         // Prepare PhoneAccountHandle for the new sim event
280         PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(
281                 new ComponentName(TELEPHONY_PACKAGE, TELEPHONY_CLASS),
282                         TEST_PHONE_ACCOUNT_HANDLE_SUB_ID);
283         Intent intent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED);
284         intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
285         mBroadcastReceiver.onReceive(mTestContext, intent);
286 
287         // Check four coloumns of Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING have migrated
288         assertEquals(4 /** entries in the preconfigured database file */,
289                 DatabaseUtils.longForQuery(sqLiteDatabase,
290                         "select count(*) from " + ContactsDatabaseHelper.Tables.DATA
291                                 + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING
292                                         + " = 0", null));
293         // Check two coloumns
294         // of Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING have not migrated
295         assertEquals(2 /** pending migration entries after migration in the preconfigured file */,
296                 DatabaseUtils.longForQuery(sqLiteDatabase,
297                         "select count(*) from " + ContactsDatabaseHelper.Tables.DATA
298                                 + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING
299                                         + " = 1", null));
300 
301         mContactsProvider2.setContactsDatabaseHelperForTest(originalContactsDatabaseHelper);
302     }
303 
testPhoneAccountHandleMigrationInitiation()304     public void testPhoneAccountHandleMigrationInitiation() throws IOException {
305         ContactsDatabaseHelper originalContactsDatabaseHelper
306                 = mContactsProvider2.getContactsDatabaseHelperForTest();
307 
308         // Mock SubscriptionManager
309         SubscriptionInfo subscriptionInfo = new SubscriptionInfo(
310                 TEST_PHONE_ACCOUNT_HANDLE_SUB_ID_INT, TEST_PHONE_ACCOUNT_HANDLE_ICC_ID1,
311                         1, "a", "b", 1, 1, "test", 1, null, null, null, null, false, null, null);
312         List<SubscriptionInfo> subscriptionInfoList = new ArrayList<>();
313         subscriptionInfoList.add(subscriptionInfo);
314         when(mSubscriptionManager.getAllSubscriptionInfoList()).thenReturn(subscriptionInfoList);
315 
316         // Mock ContactsDatabaseHelper
317         ContactsDatabaseHelper contactsDatabaseHelper = getMockContactsDatabaseHelper(
318                 "testContactsPhoneAccountHandleMigrationInitiation.db");
319 
320         // Test setPhoneAccountMigrationStatusPending as false
321         PhoneAccountHandleMigrationUtils phoneAccountHandleMigrationUtils
322                 = contactsDatabaseHelper.getPhoneAccountHandleMigrationUtils();
323 
324         // Test ContactsDatabaseHelper.isPhoneAccountMigrationPending as true
325         // and set for testing migration logic
326         phoneAccountHandleMigrationUtils.setPhoneAccountMigrationStatusPending(true);
327 
328         mContactsProvider2.setContactsDatabaseHelperForTest(contactsDatabaseHelper);
329         final SQLiteDatabase sqLiteDatabase = contactsDatabaseHelper.getReadableDatabase();
330 
331         // Check each entry in the Data has a new coloumn of
332         // Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING that has a value of 1
333         assertEquals(6, DatabaseUtils.longForQuery(sqLiteDatabase,
334                 "select count(*) from " + ContactsDatabaseHelper.Tables.DATA
335                         + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING
336                                 + " = 1", null));
337 
338         // Prepare Task for BACKGROUND_TASK_MIGRATE_PHONE_ACCOUNT_HANDLES
339         mContactsProvider2.performBackgroundTask(
340                 mContactsProvider2.BACKGROUND_TASK_MIGRATE_PHONE_ACCOUNT_HANDLES, null);
341 
342         // Check four coloumns of Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING have migrated
343         assertEquals(4, DatabaseUtils.longForQuery(sqLiteDatabase,
344                 "select count(*) from " + ContactsDatabaseHelper.Tables.DATA
345                         + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING
346                                 + " = 0", null));
347         // Check two coloumns of Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING have not migrated
348         assertEquals(2, DatabaseUtils.longForQuery(sqLiteDatabase,
349                 "select count(*) from " + ContactsDatabaseHelper.Tables.DATA
350                         + " where " + Data.IS_PHONE_ACCOUNT_MIGRATION_PENDING
351                                 + " = 1", null));
352 
353         // Verify the pending status of phone account migration.
354         assertTrue(phoneAccountHandleMigrationUtils.isPhoneAccountMigrationPending());
355 
356         mContactsProvider2.setContactsDatabaseHelperForTest(originalContactsDatabaseHelper);
357     }
358 
testPhoneAccountHandleMigrationPendingStatus()359     public void testPhoneAccountHandleMigrationPendingStatus() {
360         // Mock ContactsDatabaseHelper
361         ContactsDatabaseHelper contactsDatabaseHelper = getMockContactsDatabaseHelper(
362                 "testPhoneAccountHandleMigrationPendingStatus.db");
363 
364         // Test setPhoneAccountMigrationStatusPending as false
365         PhoneAccountHandleMigrationUtils phoneAccountHandleMigrationUtils
366                 = contactsDatabaseHelper.getPhoneAccountHandleMigrationUtils();
367         phoneAccountHandleMigrationUtils.setPhoneAccountMigrationStatusPending(false);
368         assertFalse(phoneAccountHandleMigrationUtils.isPhoneAccountMigrationPending());
369 
370         // Test ContactsDatabaseHelper.isPhoneAccountMigrationPending as true
371         // and set for testing migration logic
372         phoneAccountHandleMigrationUtils.setPhoneAccountMigrationStatusPending(true);
373         assertTrue(phoneAccountHandleMigrationUtils.isPhoneAccountMigrationPending());
374     }
375 
testConvertEnterpriseUriWithEnterpriseDirectoryToLocalUri()376     public void testConvertEnterpriseUriWithEnterpriseDirectoryToLocalUri() {
377         String phoneNumber = "886";
378         String directory = String.valueOf(Directory.ENTERPRISE_DEFAULT);
379         boolean isSip = true;
380         Uri enterpriseUri = Phone.ENTERPRISE_CONTENT_URI.buildUpon().appendPath(phoneNumber)
381                 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, directory)
382                 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
383                         String.valueOf(isSip)).build();
384         Uri localUri = ContactsProvider2.convertToLocalUri(enterpriseUri, Phone.CONTENT_URI);
385         Uri expectedUri = Phone.CONTENT_URI.buildUpon().appendPath(phoneNumber)
386                 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
387                         String.valueOf(Directory.DEFAULT))
388                 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
389                         String.valueOf(isSip)).build();
390         assertUriEquals(expectedUri, localUri);
391     }
392 
testConvertEnterpriseUriWithPersonalDirectoryToLocalUri()393     public void testConvertEnterpriseUriWithPersonalDirectoryToLocalUri() {
394         String phoneNumber = "886";
395         String directory = String.valueOf(Directory.DEFAULT);
396         boolean isSip = true;
397         Uri enterpriseUri = Phone.ENTERPRISE_CONTENT_URI.buildUpon().appendPath(phoneNumber)
398                 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, directory)
399                 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
400                         String.valueOf(isSip)).build();
401         Uri localUri = ContactsProvider2.convertToLocalUri(enterpriseUri, Phone.CONTENT_URI);
402         Uri expectedUri = Phone.CONTENT_URI.buildUpon().appendPath(phoneNumber)
403                 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
404                         String.valueOf(Directory.DEFAULT))
405                 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
406                         String.valueOf(isSip)).build();
407         assertUriEquals(expectedUri, localUri);
408     }
409 
testConvertEnterpriseUriWithoutDirectoryToLocalUri()410     public void testConvertEnterpriseUriWithoutDirectoryToLocalUri() {
411         String phoneNumber = "886";
412         boolean isSip = true;
413         Uri enterpriseUri = Phone.ENTERPRISE_CONTENT_URI.buildUpon().appendPath(phoneNumber)
414                 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
415                         String.valueOf(isSip)).build();
416         Uri localUri = ContactsProvider2.convertToLocalUri(enterpriseUri, Phone.CONTENT_URI);
417         Uri expectedUri = Phone.CONTENT_URI.buildUpon().appendPath(phoneNumber)
418                 .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS,
419                         String.valueOf(isSip)).build();
420         assertUriEquals(expectedUri, localUri);
421     }
422 
testContactsProjection()423     public void testContactsProjection() {
424         assertProjection(Contacts.CONTENT_URI, new String[]{
425                 Contacts._ID,
426                 Contacts.DISPLAY_NAME_PRIMARY,
427                 Contacts.DISPLAY_NAME_ALTERNATIVE,
428                 Contacts.DISPLAY_NAME_SOURCE,
429                 Contacts.PHONETIC_NAME,
430                 Contacts.PHONETIC_NAME_STYLE,
431                 Contacts.SORT_KEY_PRIMARY,
432                 Contacts.SORT_KEY_ALTERNATIVE,
433                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
434                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
435                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
436                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
437                 Contacts.LAST_TIME_CONTACTED,
438                 Contacts.TIMES_CONTACTED,
439                 Contacts.STARRED,
440                 Contacts.PINNED,
441                 Contacts.IN_DEFAULT_DIRECTORY,
442                 Contacts.IN_VISIBLE_GROUP,
443                 Contacts.PHOTO_ID,
444                 Contacts.PHOTO_FILE_ID,
445                 Contacts.PHOTO_URI,
446                 Contacts.PHOTO_THUMBNAIL_URI,
447                 Contacts.CUSTOM_RINGTONE,
448                 Contacts.HAS_PHONE_NUMBER,
449                 Contacts.SEND_TO_VOICEMAIL,
450                 Contacts.IS_USER_PROFILE,
451                 Contacts.LOOKUP_KEY,
452                 Contacts.NAME_RAW_CONTACT_ID,
453                 Contacts.CONTACT_PRESENCE,
454                 Contacts.CONTACT_CHAT_CAPABILITY,
455                 Contacts.CONTACT_STATUS,
456                 Contacts.CONTACT_STATUS_TIMESTAMP,
457                 Contacts.CONTACT_STATUS_RES_PACKAGE,
458                 Contacts.CONTACT_STATUS_LABEL,
459                 Contacts.CONTACT_STATUS_ICON,
460                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP
461         });
462     }
463 
testContactsStrequentProjection()464     public void testContactsStrequentProjection() {
465         assertProjection(Contacts.CONTENT_STREQUENT_URI, new String[]{
466                 Contacts._ID,
467                 Contacts.DISPLAY_NAME_PRIMARY,
468                 Contacts.DISPLAY_NAME_ALTERNATIVE,
469                 Contacts.DISPLAY_NAME_SOURCE,
470                 Contacts.PHONETIC_NAME,
471                 Contacts.PHONETIC_NAME_STYLE,
472                 Contacts.SORT_KEY_PRIMARY,
473                 Contacts.SORT_KEY_ALTERNATIVE,
474                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
475                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
476                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
477                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
478                 Contacts.LAST_TIME_CONTACTED,
479                 Contacts.TIMES_CONTACTED,
480                 Contacts.STARRED,
481                 Contacts.PINNED,
482                 Contacts.IN_DEFAULT_DIRECTORY,
483                 Contacts.IN_VISIBLE_GROUP,
484                 Contacts.PHOTO_ID,
485                 Contacts.PHOTO_FILE_ID,
486                 Contacts.PHOTO_URI,
487                 Contacts.PHOTO_THUMBNAIL_URI,
488                 Contacts.CUSTOM_RINGTONE,
489                 Contacts.HAS_PHONE_NUMBER,
490                 Contacts.SEND_TO_VOICEMAIL,
491                 Contacts.IS_USER_PROFILE,
492                 Contacts.LOOKUP_KEY,
493                 Contacts.NAME_RAW_CONTACT_ID,
494                 Contacts.CONTACT_PRESENCE,
495                 Contacts.CONTACT_CHAT_CAPABILITY,
496                 Contacts.CONTACT_STATUS,
497                 Contacts.CONTACT_STATUS_TIMESTAMP,
498                 Contacts.CONTACT_STATUS_RES_PACKAGE,
499                 Contacts.CONTACT_STATUS_LABEL,
500                 Contacts.CONTACT_STATUS_ICON,
501                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
502                 DataUsageStatColumns.LR_TIMES_USED,
503                 DataUsageStatColumns.LR_LAST_TIME_USED,
504         });
505     }
506 
testContactsStrequentPhoneOnlyProjection()507     public void testContactsStrequentPhoneOnlyProjection() {
508         assertProjection(Contacts.CONTENT_STREQUENT_URI.buildUpon()
509                     .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build(),
510                 new String[] {
511                 Contacts._ID,
512                 Contacts.DISPLAY_NAME_PRIMARY,
513                 Contacts.DISPLAY_NAME_ALTERNATIVE,
514                 Contacts.DISPLAY_NAME_SOURCE,
515                 Contacts.PHONETIC_NAME,
516                 Contacts.PHONETIC_NAME_STYLE,
517                 Contacts.SORT_KEY_PRIMARY,
518                 Contacts.SORT_KEY_ALTERNATIVE,
519                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
520                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
521                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
522                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
523                 Contacts.LAST_TIME_CONTACTED,
524                 Contacts.TIMES_CONTACTED,
525                 Contacts.STARRED,
526                 Contacts.PINNED,
527                 Contacts.IN_DEFAULT_DIRECTORY,
528                 Contacts.IN_VISIBLE_GROUP,
529                 Contacts.PHOTO_ID,
530                 Contacts.PHOTO_FILE_ID,
531                 Contacts.PHOTO_URI,
532                 Contacts.PHOTO_THUMBNAIL_URI,
533                 Contacts.CUSTOM_RINGTONE,
534                 Contacts.HAS_PHONE_NUMBER,
535                 Contacts.SEND_TO_VOICEMAIL,
536                 Contacts.IS_USER_PROFILE,
537                 Contacts.LOOKUP_KEY,
538                 Contacts.NAME_RAW_CONTACT_ID,
539                 Contacts.CONTACT_PRESENCE,
540                 Contacts.CONTACT_CHAT_CAPABILITY,
541                 Contacts.CONTACT_STATUS,
542                 Contacts.CONTACT_STATUS_TIMESTAMP,
543                 Contacts.CONTACT_STATUS_RES_PACKAGE,
544                 Contacts.CONTACT_STATUS_LABEL,
545                 Contacts.CONTACT_STATUS_ICON,
546                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
547                 DataUsageStatColumns.LR_TIMES_USED,
548                 DataUsageStatColumns.LR_LAST_TIME_USED,
549                 Phone.NUMBER,
550                 Phone.TYPE,
551                 Phone.LABEL,
552                 Phone.IS_SUPER_PRIMARY,
553                 Phone.CONTACT_ID
554         });
555     }
556 
testContactsWithSnippetProjection()557     public void testContactsWithSnippetProjection() {
558         assertProjection(Contacts.CONTENT_FILTER_URI.buildUpon().appendPath("nothing").build(),
559             new String[]{
560                 Contacts._ID,
561                 Contacts.DISPLAY_NAME_PRIMARY,
562                 Contacts.DISPLAY_NAME_ALTERNATIVE,
563                 Contacts.DISPLAY_NAME_SOURCE,
564                 Contacts.PHONETIC_NAME,
565                 Contacts.PHONETIC_NAME_STYLE,
566                 Contacts.SORT_KEY_PRIMARY,
567                 Contacts.SORT_KEY_ALTERNATIVE,
568                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
569                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
570                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
571                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
572                 Contacts.LAST_TIME_CONTACTED,
573                 Contacts.TIMES_CONTACTED,
574                 Contacts.STARRED,
575                 Contacts.PINNED,
576                 Contacts.IN_DEFAULT_DIRECTORY,
577                 Contacts.IN_VISIBLE_GROUP,
578                 Contacts.PHOTO_ID,
579                 Contacts.PHOTO_FILE_ID,
580                 Contacts.PHOTO_URI,
581                 Contacts.PHOTO_THUMBNAIL_URI,
582                 Contacts.CUSTOM_RINGTONE,
583                 Contacts.HAS_PHONE_NUMBER,
584                 Contacts.SEND_TO_VOICEMAIL,
585                 Contacts.IS_USER_PROFILE,
586                 Contacts.LOOKUP_KEY,
587                 Contacts.NAME_RAW_CONTACT_ID,
588                 Contacts.CONTACT_PRESENCE,
589                 Contacts.CONTACT_CHAT_CAPABILITY,
590                 Contacts.CONTACT_STATUS,
591                 Contacts.CONTACT_STATUS_TIMESTAMP,
592                 Contacts.CONTACT_STATUS_RES_PACKAGE,
593                 Contacts.CONTACT_STATUS_LABEL,
594                 Contacts.CONTACT_STATUS_ICON,
595                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
596                 SearchSnippets.SNIPPET,
597         });
598     }
599 
testRawContactsProjection()600     public void testRawContactsProjection() {
601         assertProjection(RawContacts.CONTENT_URI, new String[]{
602                 RawContacts._ID,
603                 RawContacts.CONTACT_ID,
604                 RawContacts.ACCOUNT_NAME,
605                 RawContacts.ACCOUNT_TYPE,
606                 RawContacts.DATA_SET,
607                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
608                 RawContacts.SOURCE_ID,
609                 RawContacts.BACKUP_ID,
610                 RawContacts.VERSION,
611                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
612                 RawContacts.DIRTY,
613                 RawContacts.METADATA_DIRTY,
614                 RawContacts.DELETED,
615                 RawContacts.DISPLAY_NAME_PRIMARY,
616                 RawContacts.DISPLAY_NAME_ALTERNATIVE,
617                 RawContacts.DISPLAY_NAME_SOURCE,
618                 RawContacts.PHONETIC_NAME,
619                 RawContacts.PHONETIC_NAME_STYLE,
620                 RawContacts.SORT_KEY_PRIMARY,
621                 RawContacts.SORT_KEY_ALTERNATIVE,
622                 RawContactsColumns.PHONEBOOK_LABEL_PRIMARY,
623                 RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
624                 RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
625                 RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
626                 RawContacts.TIMES_CONTACTED,
627                 RawContacts.LAST_TIME_CONTACTED,
628                 RawContacts.CUSTOM_RINGTONE,
629                 RawContacts.SEND_TO_VOICEMAIL,
630                 RawContacts.STARRED,
631                 RawContacts.PINNED,
632                 RawContacts.AGGREGATION_MODE,
633                 RawContacts.SYNC1,
634                 RawContacts.SYNC2,
635                 RawContacts.SYNC3,
636                 RawContacts.SYNC4,
637         });
638     }
639 
testDataProjection()640     public void testDataProjection() {
641         assertProjection(Data.CONTENT_URI, new String[]{
642                 Data._ID,
643                 Data.RAW_CONTACT_ID,
644                 Data.HASH_ID,
645                 Data.DATA_VERSION,
646                 Data.IS_PRIMARY,
647                 Data.IS_SUPER_PRIMARY,
648                 Data.RES_PACKAGE,
649                 Data.MIMETYPE,
650                 Data.DATA1,
651                 Data.DATA2,
652                 Data.DATA3,
653                 Data.DATA4,
654                 Data.DATA5,
655                 Data.DATA6,
656                 Data.DATA7,
657                 Data.DATA8,
658                 Data.DATA9,
659                 Data.DATA10,
660                 Data.DATA11,
661                 Data.DATA12,
662                 Data.DATA13,
663                 Data.DATA14,
664                 Data.DATA15,
665                 Data.CARRIER_PRESENCE,
666                 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
667                 Data.PREFERRED_PHONE_ACCOUNT_ID,
668                 Data.SYNC1,
669                 Data.SYNC2,
670                 Data.SYNC3,
671                 Data.SYNC4,
672                 Data.CONTACT_ID,
673                 Data.PRESENCE,
674                 Data.CHAT_CAPABILITY,
675                 Data.STATUS,
676                 Data.STATUS_TIMESTAMP,
677                 Data.STATUS_RES_PACKAGE,
678                 Data.STATUS_LABEL,
679                 Data.STATUS_ICON,
680                 Data.TIMES_USED,
681                 Data.LAST_TIME_USED,
682                 RawContacts.ACCOUNT_NAME,
683                 RawContacts.ACCOUNT_TYPE,
684                 RawContacts.DATA_SET,
685                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
686                 RawContacts.SOURCE_ID,
687                 RawContacts.BACKUP_ID,
688                 RawContacts.VERSION,
689                 RawContacts.DIRTY,
690                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
691                 Contacts._ID,
692                 Contacts.DISPLAY_NAME_PRIMARY,
693                 Contacts.DISPLAY_NAME_ALTERNATIVE,
694                 Contacts.DISPLAY_NAME_SOURCE,
695                 Contacts.PHONETIC_NAME,
696                 Contacts.PHONETIC_NAME_STYLE,
697                 Contacts.SORT_KEY_PRIMARY,
698                 Contacts.SORT_KEY_ALTERNATIVE,
699                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
700                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
701                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
702                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
703                 Contacts.LAST_TIME_CONTACTED,
704                 Contacts.TIMES_CONTACTED,
705                 Contacts.STARRED,
706                 Contacts.PINNED,
707                 Contacts.IN_DEFAULT_DIRECTORY,
708                 Contacts.IN_VISIBLE_GROUP,
709                 Contacts.PHOTO_ID,
710                 Contacts.PHOTO_FILE_ID,
711                 Contacts.PHOTO_URI,
712                 Contacts.PHOTO_THUMBNAIL_URI,
713                 Contacts.CUSTOM_RINGTONE,
714                 Contacts.SEND_TO_VOICEMAIL,
715                 Contacts.LOOKUP_KEY,
716                 Contacts.NAME_RAW_CONTACT_ID,
717                 Contacts.HAS_PHONE_NUMBER,
718                 Contacts.CONTACT_PRESENCE,
719                 Contacts.CONTACT_CHAT_CAPABILITY,
720                 Contacts.CONTACT_STATUS,
721                 Contacts.CONTACT_STATUS_TIMESTAMP,
722                 Contacts.CONTACT_STATUS_RES_PACKAGE,
723                 Contacts.CONTACT_STATUS_LABEL,
724                 Contacts.CONTACT_STATUS_ICON,
725                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
726                 GroupMembership.GROUP_SOURCE_ID,
727         });
728     }
729 
testDistinctDataProjection()730     public void testDistinctDataProjection() {
731         assertProjection(Phone.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(),
732             new String[]{
733                 Data._ID,
734                 Data.HASH_ID,
735                 Data.DATA_VERSION,
736                 Data.IS_PRIMARY,
737                 Data.IS_SUPER_PRIMARY,
738                 Data.RES_PACKAGE,
739                 Data.MIMETYPE,
740                 Data.DATA1,
741                 Data.DATA2,
742                 Data.DATA3,
743                 Data.DATA4,
744                 Data.DATA5,
745                 Data.DATA6,
746                 Data.DATA7,
747                 Data.DATA8,
748                 Data.DATA9,
749                 Data.DATA10,
750                 Data.DATA11,
751                 Data.DATA12,
752                 Data.DATA13,
753                 Data.DATA14,
754                 Data.DATA15,
755                 Data.CARRIER_PRESENCE,
756                 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
757                 Data.PREFERRED_PHONE_ACCOUNT_ID,
758                 Data.SYNC1,
759                 Data.SYNC2,
760                 Data.SYNC3,
761                 Data.SYNC4,
762                 Data.CONTACT_ID,
763                 Data.PRESENCE,
764                 Data.CHAT_CAPABILITY,
765                 Data.STATUS,
766                 Data.STATUS_TIMESTAMP,
767                 Data.STATUS_RES_PACKAGE,
768                 Data.STATUS_LABEL,
769                 Data.STATUS_ICON,
770                 Data.TIMES_USED,
771                 Data.LAST_TIME_USED,
772                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
773                 Contacts._ID,
774                 Contacts.DISPLAY_NAME_PRIMARY,
775                 Contacts.DISPLAY_NAME_ALTERNATIVE,
776                 Contacts.DISPLAY_NAME_SOURCE,
777                 Contacts.PHONETIC_NAME,
778                 Contacts.PHONETIC_NAME_STYLE,
779                 Contacts.SORT_KEY_PRIMARY,
780                 Contacts.SORT_KEY_ALTERNATIVE,
781                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
782                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
783                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
784                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
785                 Contacts.LAST_TIME_CONTACTED,
786                 Contacts.TIMES_CONTACTED,
787                 Contacts.STARRED,
788                 Contacts.PINNED,
789                 Contacts.IN_DEFAULT_DIRECTORY,
790                 Contacts.IN_VISIBLE_GROUP,
791                 Contacts.PHOTO_ID,
792                 Contacts.PHOTO_FILE_ID,
793                 Contacts.PHOTO_URI,
794                 Contacts.PHOTO_THUMBNAIL_URI,
795                 Contacts.HAS_PHONE_NUMBER,
796                 Contacts.CUSTOM_RINGTONE,
797                 Contacts.SEND_TO_VOICEMAIL,
798                 Contacts.LOOKUP_KEY,
799                 Contacts.CONTACT_PRESENCE,
800                 Contacts.CONTACT_CHAT_CAPABILITY,
801                 Contacts.CONTACT_STATUS,
802                 Contacts.CONTACT_STATUS_TIMESTAMP,
803                 Contacts.CONTACT_STATUS_RES_PACKAGE,
804                 Contacts.CONTACT_STATUS_LABEL,
805                 Contacts.CONTACT_STATUS_ICON,
806                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
807                 GroupMembership.GROUP_SOURCE_ID,
808         });
809     }
810 
testEntityProjection()811     public void testEntityProjection() {
812         assertProjection(
813             Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, 0),
814                     Contacts.Entity.CONTENT_DIRECTORY),
815             new String[]{
816                 Contacts.Entity._ID,
817                 Contacts.Entity.DATA_ID,
818                 Contacts.Entity.RAW_CONTACT_ID,
819                 Data.DATA_VERSION,
820                 Data.IS_PRIMARY,
821                 Data.IS_SUPER_PRIMARY,
822                 Data.RES_PACKAGE,
823                 Data.MIMETYPE,
824                 Data.DATA1,
825                 Data.DATA2,
826                 Data.DATA3,
827                 Data.DATA4,
828                 Data.DATA5,
829                 Data.DATA6,
830                 Data.DATA7,
831                 Data.DATA8,
832                 Data.DATA9,
833                 Data.DATA10,
834                 Data.DATA11,
835                 Data.DATA12,
836                 Data.DATA13,
837                 Data.DATA14,
838                 Data.DATA15,
839                 Data.CARRIER_PRESENCE,
840                 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
841                 Data.PREFERRED_PHONE_ACCOUNT_ID,
842                 Data.SYNC1,
843                 Data.SYNC2,
844                 Data.SYNC3,
845                 Data.SYNC4,
846                 Data.CONTACT_ID,
847                 Data.PRESENCE,
848                 Data.CHAT_CAPABILITY,
849                 Data.STATUS,
850                 Data.STATUS_TIMESTAMP,
851                 Data.STATUS_RES_PACKAGE,
852                 Data.STATUS_LABEL,
853                 Data.STATUS_ICON,
854                 RawContacts.ACCOUNT_NAME,
855                 RawContacts.ACCOUNT_TYPE,
856                 RawContacts.DATA_SET,
857                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
858                 RawContacts.SOURCE_ID,
859                 RawContacts.BACKUP_ID,
860                 RawContacts.VERSION,
861                 RawContacts.DELETED,
862                 RawContacts.DIRTY,
863                 RawContacts.SYNC1,
864                 RawContacts.SYNC2,
865                 RawContacts.SYNC3,
866                 RawContacts.SYNC4,
867                 Contacts._ID,
868                 Contacts.DISPLAY_NAME_PRIMARY,
869                 Contacts.DISPLAY_NAME_ALTERNATIVE,
870                 Contacts.DISPLAY_NAME_SOURCE,
871                 Contacts.PHONETIC_NAME,
872                 Contacts.PHONETIC_NAME_STYLE,
873                 Contacts.SORT_KEY_PRIMARY,
874                 Contacts.SORT_KEY_ALTERNATIVE,
875                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
876                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
877                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
878                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
879                 Contacts.LAST_TIME_CONTACTED,
880                 Contacts.TIMES_CONTACTED,
881                 Contacts.STARRED,
882                 Contacts.PINNED,
883                 Contacts.IN_DEFAULT_DIRECTORY,
884                 Contacts.IN_VISIBLE_GROUP,
885                 Contacts.PHOTO_ID,
886                 Contacts.PHOTO_FILE_ID,
887                 Contacts.PHOTO_URI,
888                 Contacts.PHOTO_THUMBNAIL_URI,
889                 Contacts.CUSTOM_RINGTONE,
890                 Contacts.SEND_TO_VOICEMAIL,
891                 Contacts.IS_USER_PROFILE,
892                 Contacts.LOOKUP_KEY,
893                 Contacts.NAME_RAW_CONTACT_ID,
894                 Contacts.HAS_PHONE_NUMBER,
895                 Contacts.CONTACT_PRESENCE,
896                 Contacts.CONTACT_CHAT_CAPABILITY,
897                 Contacts.CONTACT_STATUS,
898                 Contacts.CONTACT_STATUS_TIMESTAMP,
899                 Contacts.CONTACT_STATUS_RES_PACKAGE,
900                 Contacts.CONTACT_STATUS_LABEL,
901                 Contacts.CONTACT_STATUS_ICON,
902                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
903                 GroupMembership.GROUP_SOURCE_ID,
904                 Contacts.Entity.TIMES_USED,
905                 Contacts.Entity.LAST_TIME_USED,
906         });
907     }
908 
testRawEntityProjection()909     public void testRawEntityProjection() {
910         assertProjection(RawContactsEntity.CONTENT_URI, new String[]{
911                 RawContacts.Entity.DATA_ID,
912                 RawContacts._ID,
913                 RawContacts.CONTACT_ID,
914                 RawContacts.ACCOUNT_NAME,
915                 RawContacts.ACCOUNT_TYPE,
916                 RawContacts.DATA_SET,
917                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
918                 RawContacts.SOURCE_ID,
919                 RawContacts.BACKUP_ID,
920                 RawContacts.VERSION,
921                 RawContacts.DIRTY,
922                 RawContacts.DELETED,
923                 RawContacts.SYNC1,
924                 RawContacts.SYNC2,
925                 RawContacts.SYNC3,
926                 RawContacts.SYNC4,
927                 RawContacts.STARRED,
928                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
929                 Data.DATA_VERSION,
930                 Data.IS_PRIMARY,
931                 Data.IS_SUPER_PRIMARY,
932                 Data.RES_PACKAGE,
933                 Data.MIMETYPE,
934                 Data.DATA1,
935                 Data.DATA2,
936                 Data.DATA3,
937                 Data.DATA4,
938                 Data.DATA5,
939                 Data.DATA6,
940                 Data.DATA7,
941                 Data.DATA8,
942                 Data.DATA9,
943                 Data.DATA10,
944                 Data.DATA11,
945                 Data.DATA12,
946                 Data.DATA13,
947                 Data.DATA14,
948                 Data.DATA15,
949                 Data.CARRIER_PRESENCE,
950                 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
951                 Data.PREFERRED_PHONE_ACCOUNT_ID,
952                 Data.SYNC1,
953                 Data.SYNC2,
954                 Data.SYNC3,
955                 Data.SYNC4,
956                 GroupMembership.GROUP_SOURCE_ID,
957         });
958     }
959 
testPhoneLookupProjection()960     public void testPhoneLookupProjection() {
961         assertProjection(PhoneLookup.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(),
962             new String[]{
963                 PhoneLookup._ID,
964                 PhoneLookup.CONTACT_ID,
965                 PhoneLookup.DATA_ID,
966                 PhoneLookup.LOOKUP_KEY,
967                 PhoneLookup.DISPLAY_NAME_SOURCE,
968                 PhoneLookup.DISPLAY_NAME,
969                 PhoneLookup.DISPLAY_NAME_ALTERNATIVE,
970                 PhoneLookup.PHONETIC_NAME,
971                 PhoneLookup.PHONETIC_NAME_STYLE,
972                 PhoneLookup.SORT_KEY_PRIMARY,
973                 PhoneLookup.SORT_KEY_ALTERNATIVE,
974                 PhoneLookup.LAST_TIME_CONTACTED,
975                 PhoneLookup.TIMES_CONTACTED,
976                 PhoneLookup.STARRED,
977                 PhoneLookup.IN_DEFAULT_DIRECTORY,
978                 PhoneLookup.IN_VISIBLE_GROUP,
979                 PhoneLookup.PHOTO_FILE_ID,
980                 PhoneLookup.PHOTO_ID,
981                 PhoneLookup.PHOTO_URI,
982                 PhoneLookup.PHOTO_THUMBNAIL_URI,
983                 PhoneLookup.CUSTOM_RINGTONE,
984                 PhoneLookup.HAS_PHONE_NUMBER,
985                 PhoneLookup.SEND_TO_VOICEMAIL,
986                 PhoneLookup.NUMBER,
987                 PhoneLookup.TYPE,
988                 PhoneLookup.LABEL,
989                 PhoneLookup.NORMALIZED_NUMBER,
990                 Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
991                 Data.PREFERRED_PHONE_ACCOUNT_ID,
992         });
993     }
994 
testPhoneLookupEnterpriseProjection()995     public void testPhoneLookupEnterpriseProjection() {
996         assertProjection(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI
997                         .buildUpon().appendPath("123").build(),
998                 new String[]{
999                         PhoneLookup._ID,
1000                         PhoneLookup.CONTACT_ID,
1001                         PhoneLookup.DATA_ID,
1002                         PhoneLookup.LOOKUP_KEY,
1003                         PhoneLookup.DISPLAY_NAME_SOURCE,
1004                         PhoneLookup.DISPLAY_NAME,
1005                         PhoneLookup.DISPLAY_NAME_ALTERNATIVE,
1006                         PhoneLookup.PHONETIC_NAME,
1007                         PhoneLookup.PHONETIC_NAME_STYLE,
1008                         PhoneLookup.SORT_KEY_PRIMARY,
1009                         PhoneLookup.SORT_KEY_ALTERNATIVE,
1010                         PhoneLookup.LAST_TIME_CONTACTED,
1011                         PhoneLookup.TIMES_CONTACTED,
1012                         PhoneLookup.STARRED,
1013                         PhoneLookup.IN_DEFAULT_DIRECTORY,
1014                         PhoneLookup.IN_VISIBLE_GROUP,
1015                         PhoneLookup.PHOTO_FILE_ID,
1016                         PhoneLookup.PHOTO_ID,
1017                         PhoneLookup.PHOTO_URI,
1018                         PhoneLookup.PHOTO_THUMBNAIL_URI,
1019                         PhoneLookup.CUSTOM_RINGTONE,
1020                         PhoneLookup.HAS_PHONE_NUMBER,
1021                         PhoneLookup.SEND_TO_VOICEMAIL,
1022                         PhoneLookup.NUMBER,
1023                         PhoneLookup.TYPE,
1024                         PhoneLookup.LABEL,
1025                         PhoneLookup.NORMALIZED_NUMBER,
1026                         Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
1027                         Data.PREFERRED_PHONE_ACCOUNT_ID,
1028                 });
1029     }
1030 
testSipPhoneLookupProjection()1031     public void testSipPhoneLookupProjection() {
1032         assertContainProjection(PhoneLookup.CONTENT_FILTER_URI.buildUpon().appendPath("123")
1033                         .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, "1")
1034                         .build(),
1035                 new String[] {
1036                         PhoneLookup._ID,
1037                         PhoneLookup.CONTACT_ID,
1038                         PhoneLookup.DATA_ID,
1039                         PhoneLookup.LOOKUP_KEY,
1040                         PhoneLookup.DISPLAY_NAME,
1041                         PhoneLookup.LAST_TIME_CONTACTED,
1042                         PhoneLookup.TIMES_CONTACTED,
1043                         PhoneLookup.STARRED,
1044                         PhoneLookup.IN_DEFAULT_DIRECTORY,
1045                         PhoneLookup.IN_VISIBLE_GROUP,
1046                         PhoneLookup.PHOTO_FILE_ID,
1047                         PhoneLookup.PHOTO_ID,
1048                         PhoneLookup.PHOTO_URI,
1049                         PhoneLookup.PHOTO_THUMBNAIL_URI,
1050                         PhoneLookup.CUSTOM_RINGTONE,
1051                         PhoneLookup.HAS_PHONE_NUMBER,
1052                         PhoneLookup.SEND_TO_VOICEMAIL,
1053                         PhoneLookup.NUMBER,
1054                         PhoneLookup.TYPE,
1055                         PhoneLookup.LABEL,
1056                         PhoneLookup.NORMALIZED_NUMBER,
1057                 });
1058     }
1059 
testSipPhoneLookupEnterpriseProjection()1060     public void testSipPhoneLookupEnterpriseProjection() {
1061         assertContainProjection(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI
1062                         .buildUpon()
1063                         .appendPath("123")
1064                         .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, "1")
1065                         .build(),
1066                 new String[] {
1067                         PhoneLookup._ID,
1068                         PhoneLookup.CONTACT_ID,
1069                         PhoneLookup.DATA_ID,
1070                         PhoneLookup.LOOKUP_KEY,
1071                         PhoneLookup.DISPLAY_NAME,
1072                         PhoneLookup.LAST_TIME_CONTACTED,
1073                         PhoneLookup.TIMES_CONTACTED,
1074                         PhoneLookup.STARRED,
1075                         PhoneLookup.IN_DEFAULT_DIRECTORY,
1076                         PhoneLookup.IN_VISIBLE_GROUP,
1077                         PhoneLookup.PHOTO_FILE_ID,
1078                         PhoneLookup.PHOTO_ID,
1079                         PhoneLookup.PHOTO_URI,
1080                         PhoneLookup.PHOTO_THUMBNAIL_URI,
1081                         PhoneLookup.CUSTOM_RINGTONE,
1082                         PhoneLookup.HAS_PHONE_NUMBER,
1083                         PhoneLookup.SEND_TO_VOICEMAIL,
1084                         PhoneLookup.NUMBER,
1085                         PhoneLookup.TYPE,
1086                         PhoneLookup.LABEL,
1087                         PhoneLookup.NORMALIZED_NUMBER,
1088                 });
1089     }
1090 
testGroupsProjection()1091     public void testGroupsProjection() {
1092         assertProjection(Groups.CONTENT_URI, new String[]{
1093                 Groups._ID,
1094                 Groups.ACCOUNT_NAME,
1095                 Groups.ACCOUNT_TYPE,
1096                 Groups.DATA_SET,
1097                 Groups.ACCOUNT_TYPE_AND_DATA_SET,
1098                 Groups.SOURCE_ID,
1099                 Groups.DIRTY,
1100                 Groups.VERSION,
1101                 Groups.RES_PACKAGE,
1102                 Groups.TITLE,
1103                 Groups.TITLE_RES,
1104                 Groups.GROUP_VISIBLE,
1105                 Groups.SYSTEM_ID,
1106                 Groups.DELETED,
1107                 Groups.NOTES,
1108                 Groups.SHOULD_SYNC,
1109                 Groups.FAVORITES,
1110                 Groups.AUTO_ADD,
1111                 Groups.GROUP_IS_READ_ONLY,
1112                 Groups.SYNC1,
1113                 Groups.SYNC2,
1114                 Groups.SYNC3,
1115                 Groups.SYNC4,
1116         });
1117     }
1118 
testGroupsSummaryProjection()1119     public void testGroupsSummaryProjection() {
1120         assertProjection(Groups.CONTENT_SUMMARY_URI, new String[]{
1121                 Groups._ID,
1122                 Groups.ACCOUNT_NAME,
1123                 Groups.ACCOUNT_TYPE,
1124                 Groups.DATA_SET,
1125                 Groups.ACCOUNT_TYPE_AND_DATA_SET,
1126                 Groups.SOURCE_ID,
1127                 Groups.DIRTY,
1128                 Groups.VERSION,
1129                 Groups.RES_PACKAGE,
1130                 Groups.TITLE,
1131                 Groups.TITLE_RES,
1132                 Groups.GROUP_VISIBLE,
1133                 Groups.SYSTEM_ID,
1134                 Groups.DELETED,
1135                 Groups.NOTES,
1136                 Groups.SHOULD_SYNC,
1137                 Groups.FAVORITES,
1138                 Groups.AUTO_ADD,
1139                 Groups.GROUP_IS_READ_ONLY,
1140                 Groups.SYNC1,
1141                 Groups.SYNC2,
1142                 Groups.SYNC3,
1143                 Groups.SYNC4,
1144                 Groups.SUMMARY_COUNT,
1145                 Groups.SUMMARY_WITH_PHONES,
1146                 Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
1147         });
1148     }
1149 
testAggregateExceptionProjection()1150     public void testAggregateExceptionProjection() {
1151         assertProjection(AggregationExceptions.CONTENT_URI, new String[]{
1152                 AggregationExceptionColumns._ID,
1153                 AggregationExceptions.TYPE,
1154                 AggregationExceptions.RAW_CONTACT_ID1,
1155                 AggregationExceptions.RAW_CONTACT_ID2,
1156         });
1157     }
1158 
testSettingsProjection()1159     public void testSettingsProjection() {
1160         assertProjection(Settings.CONTENT_URI, new String[]{
1161                 Settings.ACCOUNT_NAME,
1162                 Settings.ACCOUNT_TYPE,
1163                 Settings.DATA_SET,
1164                 Settings.UNGROUPED_VISIBLE,
1165                 Settings.SHOULD_SYNC,
1166                 Settings.ANY_UNSYNCED,
1167                 Settings.UNGROUPED_COUNT,
1168                 Settings.UNGROUPED_WITH_PHONES,
1169         });
1170     }
1171 
testStatusUpdatesProjection()1172     public void testStatusUpdatesProjection() {
1173         assertProjection(StatusUpdates.CONTENT_URI, new String[]{
1174                 PresenceColumns.RAW_CONTACT_ID,
1175                 StatusUpdates.DATA_ID,
1176                 StatusUpdates.IM_ACCOUNT,
1177                 StatusUpdates.IM_HANDLE,
1178                 StatusUpdates.PROTOCOL,
1179                 StatusUpdates.CUSTOM_PROTOCOL,
1180                 StatusUpdates.PRESENCE,
1181                 StatusUpdates.CHAT_CAPABILITY,
1182                 StatusUpdates.STATUS,
1183                 StatusUpdates.STATUS_TIMESTAMP,
1184                 StatusUpdates.STATUS_RES_PACKAGE,
1185                 StatusUpdates.STATUS_ICON,
1186                 StatusUpdates.STATUS_LABEL,
1187         });
1188     }
1189 
testDirectoryProjection()1190     public void testDirectoryProjection() {
1191         assertProjection(Directory.CONTENT_URI, new String[]{
1192                 Directory._ID,
1193                 Directory.PACKAGE_NAME,
1194                 Directory.TYPE_RESOURCE_ID,
1195                 Directory.DISPLAY_NAME,
1196                 Directory.DIRECTORY_AUTHORITY,
1197                 Directory.ACCOUNT_TYPE,
1198                 Directory.ACCOUNT_NAME,
1199                 Directory.EXPORT_SUPPORT,
1200                 Directory.SHORTCUT_SUPPORT,
1201                 Directory.PHOTO_SUPPORT,
1202         });
1203     }
1204 
testProviderStatusProjection()1205     public void testProviderStatusProjection() {
1206         assertProjection(ProviderStatus.CONTENT_URI, new String[]{
1207                 ProviderStatus.STATUS,
1208                 ProviderStatus.DATABASE_CREATION_TIMESTAMP,
1209         });
1210     }
1211 
testRawContactsInsert()1212     public void testRawContactsInsert() {
1213         ContentValues values = new ContentValues();
1214 
1215         values.put(RawContacts.ACCOUNT_NAME, "a");
1216         values.put(RawContacts.ACCOUNT_TYPE, "b");
1217         values.put(RawContacts.DATA_SET, "ds");
1218         values.put(RawContacts.SOURCE_ID, "c");
1219         values.put(RawContacts.VERSION, 42);
1220         values.put(RawContacts.DIRTY, 1);
1221         values.put(RawContacts.DELETED, 1);
1222         values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
1223         values.put(RawContacts.CUSTOM_RINGTONE, "d");
1224         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
1225         values.put(RawContacts.LAST_TIME_CONTACTED, 86400 + 123);
1226         values.put(RawContacts.TIMES_CONTACTED, 12);
1227         values.put(RawContacts.STARRED, 1);
1228         values.put(RawContacts.SYNC1, "e");
1229         values.put(RawContacts.SYNC2, "f");
1230         values.put(RawContacts.SYNC3, "g");
1231         values.put(RawContacts.SYNC4, "h");
1232 
1233         Uri rowUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1234         long rawContactId = ContentUris.parseId(rowUri);
1235 
1236         values.put(RawContacts.LAST_TIME_CONTACTED, 0);
1237         values.put(RawContacts.TIMES_CONTACTED, 0);
1238 
1239         assertStoredValues(rowUri, values);
1240         assertNetworkNotified(true);
1241     }
1242 
testDataDirectoryWithLookupUri()1243     public void testDataDirectoryWithLookupUri() {
1244         ContentValues values = new ContentValues();
1245 
1246         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
1247         insertPhoneNumber(rawContactId, "555-GOOG-411");
1248         insertEmail(rawContactId, "google@android.com");
1249 
1250         long contactId = queryContactId(rawContactId);
1251         String lookupKey = queryLookupKey(contactId);
1252 
1253         // Complete and valid lookup URI
1254         Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
1255         Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
1256 
1257         assertDataRows(dataUri, values);
1258 
1259         // Complete but stale lookup URI
1260         lookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
1261         dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
1262         assertDataRows(dataUri, values);
1263 
1264         // Incomplete lookup URI (lookup key only, no contact ID)
1265         dataUri = Uri.withAppendedPath(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
1266                 lookupKey), Contacts.Data.CONTENT_DIRECTORY);
1267         assertDataRows(dataUri, values);
1268     }
1269 
assertDataRows(Uri dataUri, ContentValues values)1270     private void assertDataRows(Uri dataUri, ContentValues values) {
1271         Cursor cursor = mResolver.query(dataUri, new String[]{ Data.DATA1 }, null, null, Data._ID);
1272         assertEquals(3, cursor.getCount());
1273         cursor.moveToFirst();
1274         values.put(Data.DATA1, "John Doe");
1275         assertCursorValues(cursor, values);
1276 
1277         cursor.moveToNext();
1278         values.put(Data.DATA1, "555-GOOG-411");
1279         assertCursorValues(cursor, values);
1280 
1281         cursor.moveToNext();
1282         values.put(Data.DATA1, "google@android.com");
1283         assertCursorValues(cursor, values);
1284 
1285         cursor.close();
1286     }
1287 
testContactEntitiesWithIdBasedUri()1288     public void testContactEntitiesWithIdBasedUri() {
1289         ContentValues values = new ContentValues();
1290         Account account1 = new Account("act1", "actype1");
1291         Account account2 = new Account("act2", "actype2");
1292 
1293         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, account1);
1294         insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
1295         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90,
1296                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
1297 
1298         long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2);
1299         setAggregationException(
1300                 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
1301 
1302         long contactId = queryContactId(rawContactId1);
1303 
1304         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1305         Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
1306 
1307         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
1308     }
1309 
testContactEntitiesWithLookupUri()1310     public void testContactEntitiesWithLookupUri() {
1311         ContentValues values = new ContentValues();
1312         Account account1 = new Account("act1", "actype1");
1313         Account account2 = new Account("act2", "actype2");
1314 
1315         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, account1);
1316         insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
1317         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90,
1318                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
1319 
1320         long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2);
1321         setAggregationException(
1322                 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
1323 
1324         long contactId = queryContactId(rawContactId1);
1325         String lookupKey = queryLookupKey(contactId);
1326 
1327         // First try with a matching contact ID
1328         Uri contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
1329         Uri entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
1330         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
1331 
1332         // Now try with a contact ID mismatch
1333         contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
1334         entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
1335         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
1336 
1337         // Now try without an ID altogether
1338         contactLookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
1339         entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
1340         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
1341     }
1342 
assertEntityRows(Uri entityUri, long contactId, long rawContactId1, long rawContactId2)1343     private void assertEntityRows(Uri entityUri, long contactId, long rawContactId1,
1344             long rawContactId2) {
1345         ContentValues values = new ContentValues();
1346 
1347         Cursor cursor = mResolver.query(entityUri, null, null, null,
1348                 Contacts.Entity.RAW_CONTACT_ID + "," + Contacts.Entity.DATA_ID);
1349         assertEquals(3, cursor.getCount());
1350 
1351         // First row - name
1352         cursor.moveToFirst();
1353         values.put(Contacts.Entity.CONTACT_ID, contactId);
1354         values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1);
1355         values.put(Contacts.Entity.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
1356         values.put(Contacts.Entity.DATA1, "John Doe");
1357         values.put(Contacts.Entity.ACCOUNT_NAME, "act1");
1358         values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1");
1359         values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
1360         values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
1361         values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
1362         values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
1363         values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
1364         values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
1365         values.putNull(Contacts.Entity.PRESENCE);
1366         assertCursorValues(cursor, values);
1367 
1368         // Second row - IM
1369         cursor.moveToNext();
1370         values.put(Contacts.Entity.CONTACT_ID, contactId);
1371         values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1);
1372         values.put(Contacts.Entity.MIMETYPE, Im.CONTENT_ITEM_TYPE);
1373         values.put(Contacts.Entity.DATA1, "gtalk");
1374         values.put(Contacts.Entity.ACCOUNT_NAME, "act1");
1375         values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1");
1376         values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
1377         values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
1378         values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
1379         values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
1380         values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
1381         values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
1382         values.put(Contacts.Entity.PRESENCE, StatusUpdates.IDLE);
1383         assertCursorValues(cursor, values);
1384 
1385         // Third row - second raw contact, not data
1386         cursor.moveToNext();
1387         values.put(Contacts.Entity.CONTACT_ID, contactId);
1388         values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId2);
1389         values.putNull(Contacts.Entity.MIMETYPE);
1390         values.putNull(Contacts.Entity.DATA_ID);
1391         values.putNull(Contacts.Entity.DATA1);
1392         values.put(Contacts.Entity.ACCOUNT_NAME, "act2");
1393         values.put(Contacts.Entity.ACCOUNT_TYPE, "actype2");
1394         values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
1395         values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
1396         values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
1397         values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
1398         values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
1399         values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
1400         values.putNull(Contacts.Entity.PRESENCE);
1401         assertCursorValues(cursor, values);
1402 
1403         cursor.close();
1404     }
1405 
testDataInsert()1406     public void testDataInsert() {
1407         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe");
1408 
1409         ContentValues values = new ContentValues();
1410         putDataValues(values, rawContactId);
1411         Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
1412         long dataId = ContentUris.parseId(dataUri);
1413 
1414         long contactId = queryContactId(rawContactId);
1415         values.put(RawContacts.CONTACT_ID, contactId);
1416         assertStoredValues(dataUri, values);
1417 
1418         values.remove(Photo.PHOTO);// Remove byte[] value.
1419         assertSelection(Data.CONTENT_URI, values, Data._ID, dataId);
1420 
1421         // Access the same data through the directory under RawContacts
1422         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
1423         Uri rawContactDataUri =
1424                 Uri.withAppendedPath(rawContactUri, RawContacts.Data.CONTENT_DIRECTORY);
1425         assertSelection(rawContactDataUri, values, Data._ID, dataId);
1426 
1427         // Access the same data through the directory under Contacts
1428         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
1429         Uri contactDataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY);
1430         assertSelection(contactDataUri, values, Data._ID, dataId);
1431         assertNetworkNotified(true);
1432     }
1433 
testDataInsertAndUpdateHashId()1434     public void testDataInsertAndUpdateHashId() {
1435         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe");
1436 
1437         // Insert a data with non-photo mimetype.
1438         ContentValues values = new ContentValues();
1439         putDataValues(values, rawContactId);
1440         Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
1441 
1442         final ContactsProvider2 cp = (ContactsProvider2) getProvider();
1443         final ContactsDatabaseHelper helper = cp.getDatabaseHelper();
1444         String data1 = values.getAsString(Data.DATA1);
1445         String data2 = values.getAsString(Data.DATA2);
1446         String combineString = data1+data2;
1447         String hashId = helper.generateHashIdForData(combineString.getBytes());
1448         assertStoredValue(dataUri, Data.HASH_ID, hashId);
1449 
1450         // Update the data with primary, and check if hash_id is not changed.
1451         values.remove(Data.DATA1);
1452         values.remove(Data.DATA2);
1453         values.remove(Data.DATA15);
1454         values.put(Data.IS_PRIMARY, "1");
1455         mResolver.update(dataUri, values, null, null);
1456         assertStoredValue(dataUri, Data.IS_PRIMARY, "1");
1457         assertStoredValue(dataUri, Data.HASH_ID, hashId);
1458 
1459         // Update the data with new data1.
1460         values = new ContentValues();
1461         putDataValues(values, rawContactId);
1462         String newData1 = "Newone";
1463         values.put(Data.DATA1, newData1);
1464         mResolver.update(dataUri, values, null, null);
1465         combineString = newData1+data2;
1466         String newHashId = helper.generateHashIdForData(combineString.getBytes());
1467         assertStoredValue(dataUri, Data.HASH_ID, newHashId);
1468 
1469         // Update the data with a new Data2.
1470         values.remove(Data.DATA1);
1471         values.put(Data.DATA2, "Newtwo");
1472         combineString = "NewoneNewtwo";
1473         String testHashId = helper.generateHashIdForData(combineString.getBytes());
1474         mResolver.update(dataUri, values, null, null);
1475         assertStoredValue(dataUri, Data.HASH_ID, testHashId);
1476 
1477         // Update the data with a new data1 + data2.
1478         values.put(Data.DATA1, "one");
1479         combineString = "oneNewtwo";
1480         testHashId = helper.generateHashIdForData(combineString.getBytes());
1481         mResolver.update(dataUri, values, null, null);
1482         assertStoredValue(dataUri, Data.HASH_ID, testHashId);
1483 
1484         // Update the data with null data1 and null data2.
1485         values.putNull(Data.DATA1);
1486         values.putNull(Data.DATA2);
1487         mResolver.update(dataUri, values, null, null);
1488         assertStoredValue(dataUri, Data.HASH_ID, null);
1489     }
1490 
testDataInsertAndUpdateHashId_Photo()1491     public void testDataInsertAndUpdateHashId_Photo() {
1492         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe");
1493 
1494         // Insert a data with photo mimetype.
1495         ContentValues values = new ContentValues();
1496         values.put(Data.RAW_CONTACT_ID, rawContactId);
1497         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
1498         values.put(Data.DATA1, "testData1");
1499         values.put(Data.DATA2, "testData2");
1500         Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
1501 
1502         // Check for photo data's hashId is correct or not.
1503         final ContactsProvider2 cp = (ContactsProvider2) getProvider();
1504         final ContactsDatabaseHelper helper = cp.getDatabaseHelper();
1505         String hashId = helper.getPhotoHashId();
1506         assertStoredValue(dataUri, Data.HASH_ID, hashId);
1507 
1508         // Update the data with new DATA1, and check if hash_id is not changed.
1509         values.put(Data.DATA1, "newData1");
1510         mResolver.update(dataUri, values, null, null);
1511         assertStoredValue(dataUri, Data.DATA1, "newData1");
1512         assertStoredValue(dataUri, Data.HASH_ID, hashId);
1513     }
1514 
testDataInsertPhoneNumberTooLongIsTrimmed()1515     public void testDataInsertPhoneNumberTooLongIsTrimmed() {
1516         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe");
1517 
1518         ContentValues values = new ContentValues();
1519         values.put(Data.RAW_CONTACT_ID, rawContactId);
1520         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1521         final StringBuilder sb = new StringBuilder();
1522         for (int i = 0; i < 300; i++) {
1523             sb.append("12345");
1524         }
1525         final String phoneNumber1500Chars = sb.toString();
1526         values.put(Phone.NUMBER, phoneNumber1500Chars);
1527 
1528         Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
1529         final long dataId = ContentUris.parseId(dataUri);
1530 
1531         sb.setLength(0);
1532         for (int i = 0; i < 200; i++) {
1533             sb.append("12345");
1534         }
1535         final String phoneNumber1000Chars = sb.toString();
1536         final ContentValues expected = new ContentValues();
1537         expected.put(Phone.NUMBER, phoneNumber1000Chars);
1538         assertSelection(dataUri, expected, Data._ID, dataId);
1539     }
1540 
testRawContactDataQuery()1541     public void testRawContactDataQuery() {
1542         Account account1 = new Account("a", "b");
1543         Account account2 = new Account("c", "d");
1544         long rawContactId1 = RawContactUtil.createRawContact(mResolver, account1);
1545         Uri dataUri1 = DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Doe");
1546         long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2);
1547         Uri dataUri2 = DataUtil.insertStructuredName(mResolver, rawContactId2, "Jane", "Doe");
1548 
1549         Uri uri1 = TestUtil.maybeAddAccountQueryParameters(dataUri1, account1);
1550         Uri uri2 = TestUtil.maybeAddAccountQueryParameters(dataUri2, account2);
1551         assertStoredValue(uri1, Data._ID, ContentUris.parseId(dataUri1)) ;
1552         assertStoredValue(uri2, Data._ID, ContentUris.parseId(dataUri2)) ;
1553     }
1554 
testPhonesQuery()1555     public void testPhonesQuery() {
1556 
1557         ContentValues values = new ContentValues();
1558         values.put(RawContacts.CUSTOM_RINGTONE, "d");
1559         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
1560         values.put(RawContacts.LAST_TIME_CONTACTED, 86400 + 5);
1561         values.put(RawContacts.TIMES_CONTACTED, 54321);
1562         values.put(RawContacts.STARRED, 1);
1563 
1564         Uri rawContactUri = insertRawContact(values);
1565         long rawContactId = ContentUris.parseId(rawContactUri);
1566 
1567         DataUtil.insertStructuredName(mResolver, rawContactId, "Meghan", "Knox");
1568         Uri uri = insertPhoneNumber(rawContactId, "18004664411");
1569         long phoneId = ContentUris.parseId(uri);
1570 
1571 
1572         long contactId = queryContactId(rawContactId);
1573         values.clear();
1574         values.put(Data._ID, phoneId);
1575         values.put(Data.RAW_CONTACT_ID, rawContactId);
1576         values.put(RawContacts.CONTACT_ID, contactId);
1577         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1578         values.put(Phone.NUMBER, "18004664411");
1579         values.put(Phone.TYPE, Phone.TYPE_HOME);
1580         values.putNull(Phone.LABEL);
1581         values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
1582         values.put(Contacts.CUSTOM_RINGTONE, "d");
1583         values.put(Contacts.SEND_TO_VOICEMAIL, 1);
1584         values.put(Contacts.LAST_TIME_CONTACTED, 0);
1585         values.put(Contacts.TIMES_CONTACTED, 0);
1586         values.put(Contacts.STARRED, 1);
1587 
1588         assertStoredValues(ContentUris.withAppendedId(Phone.CONTENT_URI, phoneId), values);
1589     }
1590 
testPhonesWithMergedContacts()1591     public void testPhonesWithMergedContacts() {
1592         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
1593         insertPhoneNumber(rawContactId1, "123456789", true);
1594 
1595         long rawContactId2 = RawContactUtil.createRawContact(mResolver);
1596         insertPhoneNumber(rawContactId2, "123456789", true);
1597 
1598         setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
1599                 rawContactId1, rawContactId2);
1600         assertNotAggregated(rawContactId1, rawContactId2);
1601 
1602         ContentValues values1 = new ContentValues();
1603         values1.put(Contacts.DISPLAY_NAME, "123456789");
1604         values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1605         values1.put(Phone.NUMBER, "123456789");
1606 
1607         // There are two phone numbers, so we should get two rows.
1608         assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1});
1609 
1610         // Now set the dedupe flag.  But still we should get two rows, because they're two
1611         // different contacts.  We only dedupe within each contact.
1612         final Uri dedupeUri = Phone.CONTENT_URI.buildUpon()
1613                 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
1614                 .build();
1615         assertStoredValues(dedupeUri, new ContentValues[] {values1, values1});
1616 
1617         // Now join them into a single contact.
1618         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
1619                 rawContactId1, rawContactId2);
1620 
1621         assertAggregated(rawContactId1, rawContactId2, "123456789");
1622 
1623         // Contact merge won't affect the default result of Phone Uri, where we don't dedupe.
1624         assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1});
1625 
1626         // Now we dedupe them.
1627         assertStoredValues(dedupeUri, values1);
1628     }
1629 
testPhonesNormalizedNumber()1630     public void testPhonesNormalizedNumber() {
1631         final long rawContactId = RawContactUtil.createRawContact(mResolver);
1632 
1633         // Write both a number and a normalized number. Those should be written as-is
1634         final ContentValues values = new ContentValues();
1635         values.put(Data.RAW_CONTACT_ID, rawContactId);
1636         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1637         values.put(Phone.NUMBER, "1234");
1638         values.put(Phone.NORMALIZED_NUMBER, "5678");
1639         values.put(Phone.TYPE, Phone.TYPE_HOME);
1640 
1641         final Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
1642 
1643         // Check the lookup table.
1644         assertEquals(1,
1645                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1234"), null, null));
1646         assertEquals(1,
1647                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "5678"), null, null));
1648 
1649         // Check the data table.
1650         assertStoredValues(dataUri,
1651                 cv(Phone.NUMBER, "1234", Phone.NORMALIZED_NUMBER, "5678")
1652                 );
1653 
1654         // Replace both in an UPDATE
1655         values.clear();
1656         values.put(Phone.NUMBER, "4321");
1657         values.put(Phone.NORMALIZED_NUMBER, "8765");
1658         mResolver.update(dataUri, values, null, null);
1659         assertEquals(0,
1660                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1234"), null, null));
1661         assertEquals(1,
1662                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "4321"), null, null));
1663         assertEquals(0,
1664                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "5678"), null, null));
1665         assertEquals(1,
1666                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null));
1667 
1668         assertStoredValues(dataUri,
1669                 cv(Phone.NUMBER, "4321", Phone.NORMALIZED_NUMBER, "8765")
1670                 );
1671 
1672         // Replace only NUMBER ==> NORMALIZED_NUMBER will be inferred (we test that by making
1673         // sure the old manual value can not be found anymore)
1674         values.clear();
1675         values.put(Phone.NUMBER, "+1-800-466-5432");
1676         mResolver.update(dataUri, values, null, null);
1677         assertEquals(
1678                 1,
1679                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "+1-800-466-5432"), null,
1680                         null));
1681         assertEquals(0,
1682                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null));
1683 
1684         assertStoredValues(dataUri,
1685                 cv(Phone.NUMBER, "+1-800-466-5432", Phone.NORMALIZED_NUMBER, "+18004665432")
1686                 );
1687 
1688         // Replace only NORMALIZED_NUMBER ==> call is ignored, things will be unchanged
1689         values.clear();
1690         values.put(Phone.NORMALIZED_NUMBER, "8765");
1691         mResolver.update(dataUri, values, null, null);
1692         assertEquals(
1693                 1,
1694                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "+1-800-466-5432"), null,
1695                         null));
1696         assertEquals(0,
1697                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null));
1698 
1699         assertStoredValues(dataUri,
1700                 cv(Phone.NUMBER, "+1-800-466-5432", Phone.NORMALIZED_NUMBER, "+18004665432")
1701                 );
1702 
1703         // Replace NUMBER with an "invalid" number which can't be normalized.  It should clear
1704         // NORMALIZED_NUMBER.
1705 
1706         // 1. Set 999 to NORMALIZED_NUMBER explicitly.
1707         values.clear();
1708         values.put(Phone.NUMBER, "888");
1709         values.put(Phone.NORMALIZED_NUMBER, "999");
1710         mResolver.update(dataUri, values, null, null);
1711 
1712         assertEquals(1,
1713                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "999"), null, null));
1714 
1715         assertStoredValues(dataUri,
1716                 cv(Phone.NUMBER, "888", Phone.NORMALIZED_NUMBER, "999")
1717                 );
1718 
1719         // 2. Set an invalid number to NUMBER.
1720         values.clear();
1721         values.put(Phone.NUMBER, "1");
1722         mResolver.update(dataUri, values, null, null);
1723 
1724         assertEquals(0,
1725                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "999"), null, null));
1726 
1727         assertStoredValues(dataUri,
1728                 cv(Phone.NUMBER, "1", Phone.NORMALIZED_NUMBER, null)
1729                 );
1730     }
1731 
testPhonesFilterQuery()1732     public void testPhonesFilterQuery() {
1733         testPhonesFilterQueryInter(Phone.CONTENT_FILTER_URI);
1734     }
1735 
1736     /**
1737      * A convenient method for {@link #testPhonesFilterQuery()} and
1738      * {@link #testCallablesFilterQuery()}.
1739      *
1740      * This confirms if both URIs return identical results for phone-only contacts and
1741      * appropriately different results for contacts with sip addresses.
1742      *
1743      * @param baseFilterUri Either {@link Phone#CONTENT_FILTER_URI} or
1744      * {@link Callable#CONTENT_FILTER_URI}.
1745      */
testPhonesFilterQueryInter(Uri baseFilterUri)1746     private void testPhonesFilterQueryInter(Uri baseFilterUri) {
1747         assertTrue("Unsupported Uri (" + baseFilterUri + ")",
1748                 Phone.CONTENT_FILTER_URI.equals(baseFilterUri)
1749                         || Callable.CONTENT_FILTER_URI.equals(baseFilterUri));
1750 
1751         final long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Hot",
1752                 "Tamale", TestUtil.ACCOUNT_1);
1753         insertPhoneNumber(rawContactId1, "1-800-466-4411");
1754 
1755         final long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Chilled",
1756                 "Guacamole", TestUtil.ACCOUNT_2);
1757         insertPhoneNumber(rawContactId2, "1-800-466-5432");
1758         insertPhoneNumber(rawContactId2, "0@example.com", false, Phone.TYPE_PAGER);
1759         insertPhoneNumber(rawContactId2, "1@example.com", false, Phone.TYPE_PAGER);
1760 
1761         final Uri filterUri1 = Uri.withAppendedPath(baseFilterUri, "tamale");
1762         ContentValues values = new ContentValues();
1763         values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
1764         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1765         values.put(Phone.NUMBER, "1-800-466-4411");
1766         values.put(Phone.TYPE, Phone.TYPE_HOME);
1767         values.putNull(Phone.LABEL);
1768         assertStoredValuesWithProjection(filterUri1, values);
1769 
1770         final Uri filterUri2 = Uri.withAppendedPath(baseFilterUri, "1-800-GOOG-411");
1771         assertStoredValues(filterUri2, values);
1772 
1773         final Uri filterUri3 = Uri.withAppendedPath(baseFilterUri, "18004664");
1774         assertStoredValues(filterUri3, values);
1775 
1776         final Uri filterUri4 = Uri.withAppendedPath(baseFilterUri, "encilada");
1777         assertEquals(0, getCount(filterUri4, null, null));
1778 
1779         final Uri filterUri5 = Uri.withAppendedPath(baseFilterUri, "*");
1780         assertEquals(0, getCount(filterUri5, null, null));
1781 
1782         ContentValues values1 = new ContentValues();
1783         values1.put(Contacts.DISPLAY_NAME, "Chilled Guacamole");
1784         values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1785         values1.put(Phone.NUMBER, "1-800-466-5432");
1786         values1.put(Phone.TYPE, Phone.TYPE_HOME);
1787         values1.putNull(Phone.LABEL);
1788 
1789         ContentValues values2 = new ContentValues();
1790         values2.put(Contacts.DISPLAY_NAME, "Chilled Guacamole");
1791         values2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1792         values2.put(Phone.NUMBER, "0@example.com");
1793         values2.put(Phone.TYPE, Phone.TYPE_PAGER);
1794         values2.putNull(Phone.LABEL);
1795 
1796         ContentValues values3 = new ContentValues();
1797         values3.put(Contacts.DISPLAY_NAME, "Chilled Guacamole");
1798         values3.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
1799         values3.put(Phone.NUMBER, "1@example.com");
1800         values3.put(Phone.TYPE, Phone.TYPE_PAGER);
1801         values3.putNull(Phone.LABEL);
1802 
1803         final Uri filterUri6 = Uri.withAppendedPath(baseFilterUri, "Chilled");
1804         assertStoredValues(filterUri6, new ContentValues[]{values1, values2, values3});
1805 
1806         // Insert a SIP address. From here, Phone URI and Callable URI may return different results
1807         // than each other.
1808         insertSipAddress(rawContactId1, "sip_hot_tamale@example.com");
1809         insertSipAddress(rawContactId1, "sip:sip_hot@example.com");
1810 
1811         final Uri filterUri7 = Uri.withAppendedPath(baseFilterUri, "sip_hot");
1812         final Uri filterUri8 = Uri.withAppendedPath(baseFilterUri, "sip_hot_tamale");
1813         if (Callable.CONTENT_FILTER_URI.equals(baseFilterUri)) {
1814             ContentValues values4 = new ContentValues();
1815             values4.put(Contacts.DISPLAY_NAME, "Hot Tamale");
1816             values4.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
1817             values4.put(SipAddress.SIP_ADDRESS, "sip_hot_tamale@example.com");
1818 
1819             ContentValues values5 = new ContentValues();
1820             values5.put(Contacts.DISPLAY_NAME, "Hot Tamale");
1821             values5.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
1822             values5.put(SipAddress.SIP_ADDRESS, "sip:sip_hot@example.com");
1823             assertStoredValues(filterUri1, new ContentValues[] {values, values4, values5});
1824 
1825             assertStoredValues(filterUri7, new ContentValues[] {values4, values5});
1826             assertStoredValues(filterUri8, values4);
1827         } else {
1828             // Sip address should not affect Phone URI.
1829             assertStoredValuesWithProjection(filterUri1, values);
1830             assertEquals(0, getCount(filterUri7, null, null));
1831         }
1832 
1833         // Sanity test. Run tests for "Chilled Guacamole" again and see nothing changes
1834         // after the Sip address being inserted.
1835         assertStoredValues(filterUri2, values);
1836         assertEquals(0, getCount(filterUri4, null, null));
1837         assertEquals(0, getCount(filterUri5, null, null));
1838         assertStoredValues(filterUri6, new ContentValues[] {values1, values2, values3} );
1839     }
1840 
testPhonesFilterSearchParams()1841     public void testPhonesFilterSearchParams() {
1842         final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "Dad", null);
1843         insertPhoneNumber(rid1, "123-456-7890");
1844 
1845         final long rid2 = RawContactUtil.createRawContactWithName(mResolver, "Mam", null);
1846         insertPhoneNumber(rid2, "323-123-4567");
1847 
1848         // By default, "dad" will match both the display name and the phone number.
1849         // Because "dad" is "323" after the dialpad conversion, it'll match "Mam" too.
1850         assertStoredValues(
1851                 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad").build(),
1852                 cv(Phone.DISPLAY_NAME, "Dad", Phone.NUMBER, "123-456-7890"),
1853                 cv(Phone.DISPLAY_NAME, "Mam", Phone.NUMBER, "323-123-4567")
1854                 );
1855         assertStoredValues(
1856                 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad")
1857                     .appendQueryParameter(Phone.SEARCH_PHONE_NUMBER_KEY, "0")
1858                     .build(),
1859                 cv(Phone.DISPLAY_NAME, "Dad", Phone.NUMBER, "123-456-7890")
1860                 );
1861 
1862         assertStoredValues(
1863                 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad")
1864                     .appendQueryParameter(Phone.SEARCH_DISPLAY_NAME_KEY, "0")
1865                     .build(),
1866                 cv(Phone.DISPLAY_NAME, "Mam", Phone.NUMBER, "323-123-4567")
1867                 );
1868         assertStoredValues(
1869                 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad")
1870                         .appendQueryParameter(Phone.SEARCH_DISPLAY_NAME_KEY, "0")
1871                         .appendQueryParameter(Phone.SEARCH_PHONE_NUMBER_KEY, "0")
1872                         .build()
1873         );
1874     }
1875 
testPhoneLookup()1876     public void testPhoneLookup() {
1877         ContentValues values = new ContentValues();
1878         values.put(RawContacts.CUSTOM_RINGTONE, "d");
1879         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
1880 
1881         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1882         long rawContactId = ContentUris.parseId(rawContactUri);
1883 
1884         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale");
1885         long dataId =
1886                 Long.parseLong(insertPhoneNumber(rawContactId, "18004664411").getLastPathSegment());
1887 
1888         // We'll create two lookup records, 18004664411 and +18004664411, and the below lookup
1889         // will match both.
1890 
1891         Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
1892 
1893         values.clear();
1894         values.put(PhoneLookup._ID, queryContactId(rawContactId));
1895         values.put(PhoneLookup.CONTACT_ID, queryContactId(rawContactId));
1896         values.put(PhoneLookup.DATA_ID, dataId);
1897         values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale");
1898         values.put(PhoneLookup.NUMBER, "18004664411");
1899         values.put(PhoneLookup.TYPE, Phone.TYPE_HOME);
1900         values.putNull(PhoneLookup.LABEL);
1901         values.put(PhoneLookup.CUSTOM_RINGTONE, "d");
1902         values.put(PhoneLookup.SEND_TO_VOICEMAIL, 1);
1903         assertStoredValues(lookupUri1, null, null, new ContentValues[] {values, values});
1904 
1905         // In the context that 8004664411 is a valid number, "4664411" as a
1906         // call id should not match to either "8004664411" or "+18004664411".
1907         Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411");
1908         assertEquals(0, getCount(lookupUri2, null, null));
1909 
1910         // A wrong area code 799 vs 800 should not be matched
1911         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "7994664411");
1912         assertEquals(0, getCount(lookupUri2, null, null));
1913     }
1914 
testSipPhoneLookup()1915     public void testSipPhoneLookup() {
1916         ContentValues values = new ContentValues();
1917 
1918         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1919         long rawContactId = ContentUris.parseId(rawContactUri);
1920 
1921         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale");
1922         long dataId =
1923                 Long.parseLong(insertSipAddress(rawContactId, "abc@sip").getLastPathSegment());
1924 
1925         Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "abc@sip")
1926                             .buildUpon()
1927                             .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, "1")
1928                             .build();
1929 
1930         values.clear();
1931         values.put(PhoneLookup._ID, dataId);
1932         values.put(PhoneLookup.CONTACT_ID, queryContactId(rawContactId));
1933         values.put(PhoneLookup.DATA_ID, dataId);
1934         values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale");
1935         values.put(PhoneLookup.NUMBER, "abc@sip");
1936         values.putNull(PhoneLookup.LABEL);
1937         assertStoredValues(lookupUri1, null, null, new ContentValues[] {values});
1938 
1939         // A wrong sip address should not be matched
1940         Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "wrong@sip");
1941         assertEquals(0, getCount(lookupUri2, null, null));
1942     }
1943 
testPhoneLookupStarUseCases()1944     public void testPhoneLookupStarUseCases() {
1945         // Create two raw contacts with numbers "*123" and "12 3". This is a real life example
1946         // from b/13195334.
1947         final ContentValues values = new ContentValues();
1948         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1949         long rawContactId = ContentUris.parseId(rawContactUri);
1950         DataUtil.insertStructuredName(mResolver, rawContactId, "Emergency", /* familyName =*/ null);
1951         insertPhoneNumber(rawContactId, "*123");
1952 
1953         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1954         rawContactId = ContentUris.parseId(rawContactUri);
1955         DataUtil.insertStructuredName(mResolver, rawContactId, "Voicemail", /* familyName =*/ null);
1956         insertPhoneNumber(rawContactId, "12 3");
1957 
1958         // Verify: "123" returns the "Voicemail" raw contact id. It should not match
1959         // a phone number that starts with a "*".
1960         Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "123");
1961         values.clear();
1962         values.put(PhoneLookup.DISPLAY_NAME, "Voicemail");
1963         assertStoredValues(lookupUri, null, null, new ContentValues[] {values});
1964 
1965         lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "(1) 23");
1966         values.clear();
1967         values.put(PhoneLookup.DISPLAY_NAME, "Voicemail");
1968         assertStoredValues(lookupUri, null, null, new ContentValues[] {values});
1969 
1970         // Verify: "*123" returns the "Emergency" raw contact id.
1971         lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*1-23");
1972         values.clear();
1973         values.put(PhoneLookup.DISPLAY_NAME, "Emergency");
1974         assertStoredValues(lookupUri, null, null, new ContentValues[] {values});
1975     }
1976 
testPhoneLookupReturnsNothingRatherThanStar()1977     public void testPhoneLookupReturnsNothingRatherThanStar() {
1978         // Create Emergency raw contact with "*123456789" number.
1979         final ContentValues values = new ContentValues();
1980         final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1981         final long rawContactId1 = ContentUris.parseId(rawContactUri);
1982         DataUtil.insertStructuredName(mResolver, rawContactId1, "Emergency",
1983                 /* familyName =*/ null);
1984         insertPhoneNumber(rawContactId1, "*123456789");
1985 
1986         // Lookup should return no results. It does not ignore stars even when no other matches.
1987         final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "123456789");
1988         assertEquals(0, getCount(lookupUri, null, null));
1989     }
1990 
testPhoneLookupReturnsNothingRatherThanMissStar()1991     public void testPhoneLookupReturnsNothingRatherThanMissStar() {
1992         // Create Voice Mail raw contact with "123456789" number.
1993         final ContentValues values = new ContentValues();
1994         final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
1995         final long rawContactId1 = ContentUris.parseId(rawContactUri);
1996         DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail",
1997                 /* familyName =*/ null);
1998         insertPhoneNumber(rawContactId1, "123456789");
1999 
2000         // Lookup should return no results. It does not ignore stars even when no other matches.
2001         final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*123456789");
2002         assertEquals(0, getCount(lookupUri, null, null));
2003     }
2004 
testPhoneLookupStarNoFallbackMatch()2005     public void testPhoneLookupStarNoFallbackMatch() {
2006         final ContentValues values = new ContentValues();
2007         final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2008         final long rawContactId1 = ContentUris.parseId(rawContactUri);
2009         DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail",
2010                 /* familyName =*/ null);
2011         insertPhoneNumber(rawContactId1, "*011123456789");
2012 
2013         // The numbers "+123456789" and "*011123456789" are a "fallback" match. The + is equivalent
2014         // to "011". This lookup should return no results. Lookup does not ignore
2015         // stars, even when doing a fallback lookup.
2016         final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+123456789");
2017         assertEquals(0, getCount(lookupUri, null, null));
2018     }
2019 
testPhoneLookupStarNotBreakFallbackMatching()2020     public void testPhoneLookupStarNotBreakFallbackMatching() {
2021         // Create a raw contact with a phone number starting with "011"
2022         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
2023         long rawContactId = ContentUris.parseId(rawContactUri);
2024         DataUtil.insertStructuredName(mResolver, rawContactId, "No star",
2025                 /* familyName =*/ null);
2026         insertPhoneNumber(rawContactId, "011123456789");
2027 
2028         // Create a raw contact with a phone number starting with "*011"
2029         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
2030         rawContactId = ContentUris.parseId(rawContactUri);
2031         DataUtil.insertStructuredName(mResolver, rawContactId, "Has star",
2032                 /* familyName =*/ null);
2033         insertPhoneNumber(rawContactId, "*011123456789");
2034 
2035         // A phone number starting with "+" can (fallback) match the same phone number starting
2036         // with "001". Verify that this fallback matching still occurs in the presence of
2037         // numbers starting with "*"s.
2038         final Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
2039                 "+123456789");
2040         final ContentValues values = new ContentValues();
2041         values.put(PhoneLookup.DISPLAY_NAME, "No star");
2042         assertStoredValues(lookupUri1, null, null, new ContentValues[]{values});
2043     }
2044 
testPhoneLookupExplicitProjection()2045     public void testPhoneLookupExplicitProjection() {
2046         final ContentValues values = new ContentValues();
2047         final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2048         final long rawContactId1 = ContentUris.parseId(rawContactUri);
2049         DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail",
2050                 /* familyName =*/ null);
2051         insertPhoneNumber(rawContactId1, "+1234567");
2052 
2053         // Performing a query with a non-null projection with or without PhoneLookup.Number inside
2054         // it should not cause a crash.
2055         Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "1234567");
2056         String[] projection = new String[] {PhoneLookup.DISPLAY_NAME};
2057         mResolver.query(lookupUri, projection, null, null, null);
2058         projection = new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.NUMBER};
2059         mResolver.query(lookupUri, projection, null, null, null);
2060 
2061         // Shouldn't crash for a fallback query either
2062         lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*0111234567");
2063         projection = new String[] {PhoneLookup.DISPLAY_NAME};
2064         mResolver.query(lookupUri, projection, null, null, null);
2065         projection = new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.NUMBER};
2066         mResolver.query(lookupUri, projection, null, null, null);
2067     }
2068 
testPhoneLookupUseCases()2069     public void testPhoneLookupUseCases() {
2070         ContentValues values = new ContentValues();
2071         Uri rawContactUri;
2072         long rawContactId;
2073         Uri lookupUri2;
2074 
2075         values.put(RawContacts.CUSTOM_RINGTONE, "d");
2076         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
2077 
2078         // International format in contacts
2079         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2080         rawContactId = ContentUris.parseId(rawContactUri);
2081 
2082         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale");
2083         insertPhoneNumber(rawContactId, "+1-650-861-0000");
2084 
2085         values.clear();
2086 
2087         // match with international format
2088         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0000");
2089         assertEquals(1, getCount(lookupUri2, null, null));
2090 
2091         // match with national format
2092         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0000");
2093         assertEquals(1, getCount(lookupUri2, null, null));
2094 
2095         // does not match with wrong area code
2096         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "649 861 0000");
2097         assertEquals(0, getCount(lookupUri2, null, null));
2098 
2099         // does not match with missing digits in mistyped area code
2100         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "5 861 0000");
2101         assertEquals(0, getCount(lookupUri2, null, null));
2102 
2103         // does not match with missing digit in mistyped area code
2104         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "65 861 0000");
2105         assertEquals(0, getCount(lookupUri2, null, null));
2106 
2107         // National format in contacts
2108         values.clear();
2109         values.put(RawContacts.CUSTOM_RINGTONE, "d");
2110         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
2111         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2112         rawContactId = ContentUris.parseId(rawContactUri);
2113 
2114         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot1", "Tamale");
2115         insertPhoneNumber(rawContactId, "650-861-0001");
2116 
2117         values.clear();
2118 
2119         // match with international format
2120         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0001");
2121         assertEquals(2, getCount(lookupUri2, null, null));
2122 
2123         // match with national format
2124         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0001");
2125         assertEquals(2, getCount(lookupUri2, null, null));
2126 
2127         // Local format in contacts
2128         values.clear();
2129         values.put(RawContacts.CUSTOM_RINGTONE, "d");
2130         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
2131         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2132         rawContactId = ContentUris.parseId(rawContactUri);
2133 
2134         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot2", "Tamale");
2135         insertPhoneNumber(rawContactId, "861-0002");
2136 
2137         values.clear();
2138 
2139         // No match with international format
2140         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0002");
2141         assertEquals(0, getCount(lookupUri2, null, null));
2142 
2143         // No match with national format
2144         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0002");
2145         assertEquals(0, getCount(lookupUri2, null, null));
2146     }
2147 
testIntlPhoneLookupUseCases()2148     public void testIntlPhoneLookupUseCases() {
2149         // Checks the logic that relies on phone_number_compare_loose(Gingerbread) as a fallback
2150         //for phone number lookups.
2151         String fullNumber = "01197297427289";
2152 
2153         ContentValues values = new ContentValues();
2154         values.put(RawContacts.CUSTOM_RINGTONE, "d");
2155         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
2156         long rawContactId = ContentUris.parseId(mResolver.insert(RawContacts.CONTENT_URI, values));
2157         DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang");
2158         insertPhoneNumber(rawContactId, fullNumber);
2159 
2160         // Full number should definitely match.
2161         assertEquals(2, getCount(Uri.withAppendedPath(
2162                 PhoneLookup.CONTENT_FILTER_URI, fullNumber), null, null));
2163 
2164         // Shorter (local) number with 0 prefix should not match.
2165         assertEquals(0, getCount(Uri.withAppendedPath(
2166                 PhoneLookup.CONTENT_FILTER_URI, "097427289"), null, null));
2167 
2168         // Number with international (+972) prefix should also match.
2169         assertEquals(1, getCount(Uri.withAppendedPath(
2170                 PhoneLookup.CONTENT_FILTER_URI, "+97297427289"), null, null));
2171 
2172         // Same shorter number with dashes should not match.
2173         assertEquals(0, getCount(Uri.withAppendedPath(
2174                 PhoneLookup.CONTENT_FILTER_URI, "09-742-7289"), null, null));
2175 
2176         // Same shorter number with spaces should not match.
2177         assertEquals(0, getCount(Uri.withAppendedPath(
2178                 PhoneLookup.CONTENT_FILTER_URI, "09 742 7289"), null, null));
2179 
2180         // Some other number should not match.
2181         assertEquals(0, getCount(Uri.withAppendedPath(
2182                 PhoneLookup.CONTENT_FILTER_URI, "049102395"), null, null));
2183     }
2184 
testPhoneLookupB5252190()2185     public void testPhoneLookupB5252190() {
2186         // Test cases from b/5252190
2187         String storedNumber = "796010101";
2188 
2189         ContentValues values = new ContentValues();
2190         values.put(RawContacts.CUSTOM_RINGTONE, "d");
2191         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
2192         long rawContactId = ContentUris.parseId(mResolver.insert(RawContacts.CONTENT_URI, values));
2193         DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang");
2194         insertPhoneNumber(rawContactId, storedNumber);
2195 
2196         assertEquals(1, getCount(Uri.withAppendedPath(
2197                 PhoneLookup.CONTENT_FILTER_URI, "0796010101"), null, null));
2198 
2199         assertEquals(0, getCount(Uri.withAppendedPath(
2200                 PhoneLookup.CONTENT_FILTER_URI, "+48796010101"), null, null));
2201 
2202         assertEquals(0, getCount(Uri.withAppendedPath(
2203                 PhoneLookup.CONTENT_FILTER_URI, "48796010101"), null, null));
2204 
2205         assertEquals(0, getCount(Uri.withAppendedPath(
2206                 PhoneLookup.CONTENT_FILTER_URI, "4-879-601-0101"), null, null));
2207 
2208         assertEquals(0, getCount(Uri.withAppendedPath(
2209                 PhoneLookup.CONTENT_FILTER_URI, "4 879 601 0101"), null, null));
2210     }
2211 
testPhoneLookupUseStrictPhoneNumberCompare()2212     public void testPhoneLookupUseStrictPhoneNumberCompare() {
2213         // Test lookup cases when mUseStrictPhoneNumberComparison is true
2214         final ContactsProvider2 cp = (ContactsProvider2) getProvider();
2215         final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
2216         // Get and save the original value of mUseStrictPhoneNumberComparison so that we
2217         // can restore it when we are done with the test
2218         final boolean oldUseStrict = dbHelper.getUseStrictPhoneNumberComparisonForTest();
2219         dbHelper.setUseStrictPhoneNumberComparisonForTest(true);
2220 
2221 
2222         try {
2223             String fullNumber = "01197297427289";
2224             ContentValues values = new ContentValues();
2225             values.put(RawContacts.CUSTOM_RINGTONE, "d");
2226             values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
2227             long rawContactId = ContentUris.parseId(
2228                     mResolver.insert(RawContacts.CONTENT_URI, values));
2229             DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang");
2230             insertPhoneNumber(rawContactId, fullNumber);
2231             insertPhoneNumber(rawContactId, "5103337596");
2232             insertPhoneNumber(rawContactId, "+19012345678");
2233             // One match for full number
2234             assertEquals(1, getCount(Uri.withAppendedPath(
2235                     PhoneLookup.CONTENT_FILTER_URI, fullNumber), null, null));
2236 
2237             // No matches for extra digit at the front
2238             assertEquals(0, getCount(Uri.withAppendedPath(
2239                     PhoneLookup.CONTENT_FILTER_URI, "55103337596"), null, null));
2240             // No matches for mispelled area code
2241             assertEquals(0, getCount(Uri.withAppendedPath(
2242                     PhoneLookup.CONTENT_FILTER_URI, "5123337596"), null, null));
2243 
2244             // One match for matching number with dashes
2245             assertEquals(1, getCount(Uri.withAppendedPath(
2246                     PhoneLookup.CONTENT_FILTER_URI, "510-333-7596"), null, null));
2247 
2248             // One match for matching number with international code
2249             assertEquals(1, getCount(Uri.withAppendedPath(
2250                     PhoneLookup.CONTENT_FILTER_URI, "+1-510-333-7596"), null, null));
2251             values.clear();
2252 
2253             // No matches for extra 0 in front
2254             assertEquals(0, getCount(Uri.withAppendedPath(
2255                     PhoneLookup.CONTENT_FILTER_URI, "0-510-333-7596"), null, null));
2256             values.clear();
2257 
2258             // No matches for different country code
2259             assertEquals(0, getCount(Uri.withAppendedPath(
2260                     PhoneLookup.CONTENT_FILTER_URI, "+819012345678"), null, null));
2261             values.clear();
2262         } finally {
2263             // restore the original value of mUseStrictPhoneNumberComparison
2264             // upon test completion or failure
2265             dbHelper.setUseStrictPhoneNumberComparisonForTest(oldUseStrict);
2266         }
2267     }
2268 
2269     /**
2270      * Test for enterprise caller-id, but with no corp profile.
2271      */
testPhoneLookupEnterprise_noCorpProfile()2272     public void testPhoneLookupEnterprise_noCorpProfile() throws Exception {
2273 
2274         Uri uri1 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-111-1111");
2275 
2276         // No contacts profile, no data.
2277         assertEquals(0, getCount(uri1));
2278 
2279         // Insert a contact into the primary CP2.
2280         long rawContactId = ContentUris.parseId(
2281                 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()));
2282         DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe");
2283         insertPhoneNumber(rawContactId, "408-111-1111");
2284 
2285         // Do the query again and check the result.
2286         Cursor c = mResolver.query(uri1, null, null, null, null);
2287         try {
2288             assertEquals(1, c.getCount());
2289             c.moveToPosition(0);
2290             long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID));
2291             assertFalse(Contacts.isEnterpriseContactId(contactId)); // Make sure it's not rewritten.
2292         } finally {
2293             c.close();
2294         }
2295     }
2296 
2297     /**
2298      * Test for enterprise caller-id.  Corp profile exists, but it returns a null cursor.
2299      */
testPhoneLookupEnterprise_withCorpProfile_nullResult()2300     public void testPhoneLookupEnterprise_withCorpProfile_nullResult() throws Exception {
2301         setUpNullCorpProvider();
2302 
2303         Uri uri1 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-111-1111");
2304 
2305         // No contacts profile, no data.
2306         assertEquals(0, getCount(uri1));
2307 
2308         // Insert a contact into the primary CP2.
2309         long rawContactId = ContentUris.parseId(
2310                 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()));
2311         DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe");
2312         insertPhoneNumber(rawContactId, "408-111-1111");
2313 
2314         // Do the query again and check the result.
2315         Cursor c = mResolver.query(uri1, null, null, null, null);
2316         try {
2317             assertEquals(1, c.getCount());
2318             c.moveToPosition(0);
2319             long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID));
2320             assertFalse(Contacts.isEnterpriseContactId(contactId)); // Make sure it's not rewritten.
2321         } finally {
2322             c.close();
2323         }
2324     }
2325 
2326     /**
2327      * Set up the corp user / CP2 and returns the corp CP2 instance.
2328      *
2329      * Create a second instance of CP2, and add it to the resolver, with the "user-id@" authority.
2330      */
setUpCorpProvider()2331     private SynchronousContactsProvider2 setUpCorpProvider() throws Exception {
2332         mActor.mockUserManager.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.CORP_USER);
2333 
2334         // Note here we use a standalone CP2 so it'll have its own db helper.
2335         // Also use AlteringUserContext here to report the corp user id.
2336         final int userId = MockUserManager.CORP_USER.id;
2337         SynchronousContactsProvider2 provider = mActor.addProvider(
2338                 new SecondaryUserContactsProvider2(userId),
2339                 "" + userId + "@com.android.contacts",
2340                 new AlteringUserContext(mActor.getProviderContext(), userId));
2341         provider.wipeData();
2342         return provider;
2343     }
2344 
2345     /**
2346      * Similar to {@link #setUpCorpProvider}, but the corp CP2 set up with this will always return
2347      * null from query().
2348      */
setUpNullCorpProvider()2349     private void setUpNullCorpProvider() throws Exception {
2350         mActor.mockUserManager.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.CORP_USER);
2351 
2352         mActor.addProvider(
2353                 NullContentProvider.class,
2354                 "" + MockUserManager.CORP_USER.id + "@com.android.contacts",
2355                 new AlteringUserContext(mActor.getProviderContext(), MockUserManager.CORP_USER.id));
2356     }
2357 
2358     /**
2359      * Test for query of merged primary and work contacts.
2360      * <p/>
2361      * Note: in this test, we add one more provider instance for the authority
2362      * "10@com.android.contacts" and use it as the corp cp2.
2363      */
testQueryMergedDataPhones()2364     public void testQueryMergedDataPhones() throws Exception {
2365         mActor.addPermissions("android.permission.INTERACT_ACROSS_USERS");
2366 
2367         // Insert a contact to the primary CP2.
2368         long rawContactId = ContentUris.parseId(
2369                 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()));
2370         DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Primary");
2371 
2372         insertPhoneNumber(rawContactId, "111-111-1111", false, false, Phone.TYPE_MOBILE);
2373 
2374         // Insert a contact to the corp CP2, with different name and phone number.
2375         final SynchronousContactsProvider2 corpCp2 = setUpCorpProvider();
2376         rawContactId = ContentUris.parseId(
2377                 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues()));
2378         // Insert a name.
2379         ContentValues cv = cv(
2380                 Data.RAW_CONTACT_ID, rawContactId,
2381                 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE,
2382                 StructuredName.DISPLAY_NAME, "Contact2 Corp",
2383                 StructuredName.GIVEN_NAME, "Contact2",
2384                 StructuredName.FAMILY_NAME, "Corp");
2385         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
2386         // Insert a number.
2387         cv = cv(
2388                 Data.RAW_CONTACT_ID, rawContactId,
2389                 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE,
2390                 Phone.NUMBER, "222-222-2222",
2391                 Phone.TYPE, Phone.TYPE_MOBILE);
2392         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
2393 
2394         // Insert another contact to to corp CP2, with different name phone number and phone type
2395         rawContactId = ContentUris.parseId(
2396                 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues()));
2397         // Insert a name.
2398         cv = cv(
2399                 Data.RAW_CONTACT_ID, rawContactId,
2400                 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE,
2401                 StructuredName.DISPLAY_NAME, "Contact3 Corp",
2402                 StructuredName.GIVEN_NAME, "Contact3",
2403                 StructuredName.FAMILY_NAME, "Corp");
2404         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
2405         // Insert a number
2406         cv = cv(
2407                 Data.RAW_CONTACT_ID, rawContactId,
2408                 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE,
2409                 Phone.NUMBER, "333-333-3333",
2410                 Phone.TYPE, Phone.TYPE_HOME);
2411         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
2412 
2413         // Execute the query to get the merged result.
2414         Cursor c = mResolver.query(Phone.ENTERPRISE_CONTENT_URI, new String[]{Phone.CONTACT_ID,
2415                 Phone.DISPLAY_NAME, Phone.NUMBER}, Phone.TYPE + " = ?",
2416                 new String[]{String.valueOf(Phone.TYPE_MOBILE)}, null);
2417         try {
2418             // Verify the primary contact.
2419             assertEquals(2, c.getCount());
2420             assertEquals(3, c.getColumnCount());
2421             c.moveToPosition(0);
2422             assertEquals("Contact1 Primary", c.getString(c.getColumnIndex(Phone.DISPLAY_NAME)));
2423             assertEquals("111-111-1111", c.getString(c.getColumnIndex(Phone.NUMBER)));
2424             long contactId = c.getLong(c.getColumnIndex(Phone.CONTACT_ID));
2425             assertFalse(Contacts.isEnterpriseContactId(contactId));
2426 
2427             // Verify the enterprise contact.
2428             c.moveToPosition(1);
2429             assertEquals("Contact2 Corp", c.getString(c.getColumnIndex(Phone.DISPLAY_NAME)));
2430             assertEquals("222-222-2222", c.getString(c.getColumnIndex(Phone.NUMBER)));
2431             contactId = c.getLong(c.getColumnIndex(Phone.CONTACT_ID));
2432             assertTrue(Contacts.isEnterpriseContactId(contactId));
2433         } finally {
2434             c.close();
2435         }
2436     }
2437 
2438     /**
2439      * Test for query of merged primary and work contacts.
2440      * <p/>
2441      * Note: in this test, we add one more provider instance for the authority
2442      * "10@com.android.contacts" and use it as the corp cp2.
2443      */
testQueryMergedDataPhones_nullCorp()2444     public void testQueryMergedDataPhones_nullCorp() throws Exception {
2445         mActor.addPermissions("android.permission.INTERACT_ACROSS_USERS");
2446 
2447         // Insert a contact to the primary CP2.
2448         long rawContactId = ContentUris.parseId(
2449                 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()));
2450         DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Primary");
2451 
2452         insertPhoneNumber(rawContactId, "111-111-1111", false, false, Phone.TYPE_MOBILE);
2453 
2454         // Insert a contact to the corp CP2, with different name and phone number.
2455         setUpNullCorpProvider();
2456 
2457         // Execute the query to get the merged result.
2458         Cursor c = mResolver.query(Phone.ENTERPRISE_CONTENT_URI, new String[]{Phone.CONTACT_ID,
2459                         Phone.DISPLAY_NAME, Phone.NUMBER}, Phone.TYPE + " = ?",
2460                 new String[]{String.valueOf(Phone.TYPE_MOBILE)}, null);
2461         try {
2462             // Verify the primary contact.
2463             assertEquals(1, c.getCount());
2464             assertEquals(3, c.getColumnCount());
2465             c.moveToPosition(0);
2466             assertEquals("Contact1 Primary", c.getString(c.getColumnIndex(Phone.DISPLAY_NAME)));
2467             assertEquals("111-111-1111", c.getString(c.getColumnIndex(Phone.NUMBER)));
2468             long contactId = c.getLong(c.getColumnIndex(Phone.CONTACT_ID));
2469             assertFalse(Contacts.isEnterpriseContactId(contactId));
2470         } finally {
2471             c.close();
2472         }
2473     }
2474 
2475     /**
2476      * Test for enterprise caller-id, with the corp profile.
2477      *
2478      * Note: in this test, we add one more provider instance for the authority
2479      * "10@com.android.contacts" and use it as the corp cp2.
2480      */
testPhoneLookupEnterprise_withCorpProfile()2481     public void testPhoneLookupEnterprise_withCorpProfile() throws Exception {
2482         final SynchronousContactsProvider2 corpCp2 = setUpCorpProvider();
2483 
2484         Uri uri1 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-111-1111");
2485         Uri uri2 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-222-2222");
2486 
2487         // First, test with no contacts on either profile.
2488         assertEquals(0, getCount(uri1));
2489 
2490         // Insert a contact to the primary CP2.
2491         long rawContactId = ContentUris.parseId(
2492                 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()));
2493         DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe");
2494         insertPhoneNumber(rawContactId, "408-111-1111");
2495 
2496         // Insert a contact to the corp CP2, with the same phone number, but with a different name.
2497         rawContactId = ContentUris.parseId(
2498                 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues()));
2499         // Insert a name
2500         ContentValues cv = cv(
2501                 Data.RAW_CONTACT_ID, rawContactId,
2502                 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE,
2503                 StructuredName.DISPLAY_NAME, "Contact2 Corp",
2504                 StructuredName.GIVEN_NAME, "Contact2",
2505                 StructuredName.FAMILY_NAME, "Corp");
2506         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
2507 
2508         // Insert a number
2509         cv = cv(
2510                 Data.RAW_CONTACT_ID, rawContactId,
2511                 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE,
2512                 Phone.NUMBER, "408-111-1111",
2513                 Phone.TYPE, Phone.TYPE_HOME);
2514         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
2515 
2516         // Insert one more contact to the corp CP2, with a different number.
2517         rawContactId = ContentUris.parseId(
2518                 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues()));
2519         // Insert a name
2520         cv = cv(
2521                 Data.RAW_CONTACT_ID, rawContactId,
2522                 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE,
2523                 StructuredName.DISPLAY_NAME, "Contact3 Corp",
2524                 StructuredName.GIVEN_NAME, "Contact3",
2525                 StructuredName.FAMILY_NAME, "Corp");
2526         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
2527 
2528         // Insert a number
2529         cv = cv(
2530                 Data.RAW_CONTACT_ID, rawContactId,
2531                 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE,
2532                 Phone.NUMBER, "408-222-2222",
2533                 Phone.TYPE, Phone.TYPE_HOME);
2534         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
2535 
2536         // Okay, now execute queries and check the result.
2537 
2538         // The first URL hits the contact in the primary CP2.
2539         // There's also a contact with this phone number in the corp CP2, but that will be ignored.
2540         Cursor c = mResolver.query(uri1, null, null, null, null);
2541         try {
2542             assertEquals(1, c.getCount());
2543             c.moveToPosition(0);
2544             assertEquals("Contact1 Doe", c.getString(c.getColumnIndex(PhoneLookup.DISPLAY_NAME)));
2545 
2546             // Make sure it has a personal contact ID.
2547             long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID));
2548             assertFalse(Contacts.isEnterpriseContactId(contactId));
2549         } finally {
2550             c.close();
2551         }
2552 
2553         // Test for the second phone number, which only exists in the corp cp2.
2554         c = mResolver.query(uri2, null, null, null, null);
2555         try {
2556             // This one actually returns 2 identical rows, probably because of the join
2557             // in phone_lookup.  Callers only care the first row, so returning multiple identical
2558             // rows should be fine.
2559             assertTrue(c.getCount() > 0);
2560             c.moveToPosition(0);
2561             assertEquals("Contact3 Corp", c.getString(c.getColumnIndex(PhoneLookup.DISPLAY_NAME)));
2562 
2563             // Make sure it has a corp contact ID.
2564             long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID));
2565             assertTrue(Contacts.isEnterpriseContactId(contactId));
2566         } finally {
2567             c.close();
2568         }
2569     }
2570 
testQueryRawContactEntitiesCorp_noCorpProfile()2571     public void testQueryRawContactEntitiesCorp_noCorpProfile() {
2572         mActor.addPermissions("android.permission.INTERACT_ACROSS_USERS");
2573 
2574         // Insert a contact into the primary CP2.
2575         long rawContactId = ContentUris.parseId(
2576                 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()));
2577         DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe");
2578         insertPhoneNumber(rawContactId, "408-111-1111");
2579 
2580         // No corp profile, no data.
2581         assertEquals(0, getCount(RawContactsEntity.CORP_CONTENT_URI));
2582     }
2583 
testQueryRawContactEntitiesCorp_withCorpProfile()2584     public void testQueryRawContactEntitiesCorp_withCorpProfile() throws Exception {
2585         mActor.addPermissions("android.permission.INTERACT_ACROSS_USERS");
2586 
2587         // Insert a contact into the primary CP2.
2588         long rawContactId = ContentUris.parseId(
2589                 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()));
2590         DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe");
2591         insertPhoneNumber(rawContactId, "408-111-1111");
2592 
2593         // Insert a contact into corp CP2.
2594         final SynchronousContactsProvider2 corpCp2 = setUpCorpProvider();
2595         rawContactId = ContentUris.parseId(
2596                 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues()));
2597         // Insert a name.
2598         ContentValues cv = cv(
2599                 Data.RAW_CONTACT_ID, rawContactId,
2600                 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE,
2601                 StructuredName.DISPLAY_NAME, "Contact2 Corp");
2602         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
2603         // Insert a number.
2604         cv = cv(
2605                 Data.RAW_CONTACT_ID, rawContactId,
2606                 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE,
2607                 Phone.NUMBER, "222-222-2222",
2608                 Phone.TYPE, Phone.TYPE_MOBILE);
2609         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
2610 
2611         // Do the query
2612         Cursor c = mResolver.query(RawContactsEntity.CORP_CONTENT_URI,
2613                 new String[]{RawContactsEntity._ID, RawContactsEntity.DATA1},
2614                 RawContactsEntity.MIMETYPE + "=?", new String[]{
2615                         StructuredName.CONTENT_ITEM_TYPE}, null);
2616         // The result should only contains corp data.
2617         assertEquals(1, c.getCount());
2618         assertEquals(2, c.getColumnCount());
2619         c.moveToPosition(0);
2620         long id = c.getLong(c.getColumnIndex(RawContactsEntity._ID));
2621         String data1 = c.getString(c.getColumnIndex(RawContactsEntity.DATA1));
2622         assertEquals("Contact2 Corp", data1);
2623         assertEquals(rawContactId, id);
2624         c.close();
2625     }
2626 
testRewriteCorpDirectories()2627     public void testRewriteCorpDirectories() {
2628         // 6 columns
2629         final MatrixCursor c = new MatrixCursor(new String[] {
2630                 Directory._ID,
2631                 Directory.PACKAGE_NAME,
2632                 Directory.TYPE_RESOURCE_ID,
2633                 Directory.DISPLAY_NAME,
2634                 Directory.ACCOUNT_TYPE,
2635                 Directory.ACCOUNT_NAME,
2636         });
2637 
2638         // First, convert and make sure it returns an empty cursor.
2639         Cursor rewritten = ContactsProvider2.rewriteCorpDirectories(c);
2640 
2641         assertEquals(0, rewritten.getCount());
2642         assertEquals(6, rewritten.getColumnCount());
2643 
2644         c.addRow(new Object[] {
2645                 5L, // Directory._ID
2646                 "name", // Directory.PACKAGE_NAME
2647                 123, // Directory.TYPE_RESOURCE_ID
2648                 "display", // Directory.DISPLAY_NAME
2649                 "atype", // Directory.ACCOUNT_TYPE
2650                 "aname", // Directory.ACCOUNT_NAME
2651         });
2652 
2653         rewritten = ContactsProvider2.rewriteCorpDirectories(c);
2654         assertEquals(1, rewritten.getCount());
2655         assertEquals(6, rewritten.getColumnCount());
2656 
2657         rewritten.moveToPosition(0);
2658         int column = 0;
2659         assertEquals(1000000005L, rewritten.getLong(column++));
2660         assertEquals("name", rewritten.getString(column++));
2661         assertEquals(123, rewritten.getInt(column++));
2662         assertEquals("display", rewritten.getString(column++));
2663         assertEquals("atype", rewritten.getString(column++));
2664         assertEquals("aname", rewritten.getString(column++));
2665     }
2666 
testPhoneUpdate()2667     public void testPhoneUpdate() {
2668         ContentValues values = new ContentValues();
2669         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
2670         long rawContactId = ContentUris.parseId(rawContactUri);
2671 
2672         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale");
2673         Uri phoneUri = insertPhoneNumber(rawContactId, "18004664411");
2674 
2675         Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
2676         Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664422");
2677         assertEquals(2, getCount(lookupUri1, null, null));
2678         assertEquals(0, getCount(lookupUri2, null, null));
2679 
2680         values.clear();
2681         values.put(Phone.NUMBER, "18004664422");
2682         mResolver.update(phoneUri, values, null, null);
2683 
2684         assertEquals(0, getCount(lookupUri1, null, null));
2685         assertEquals(2, getCount(lookupUri2, null, null));
2686 
2687         // Setting number to null will remove the phone lookup record
2688         values.clear();
2689         values.putNull(Phone.NUMBER);
2690         mResolver.update(phoneUri, values, null, null);
2691 
2692         assertEquals(0, getCount(lookupUri1, null, null));
2693         assertEquals(0, getCount(lookupUri2, null, null));
2694 
2695         // Let's restore that phone lookup record
2696         values.clear();
2697         values.put(Phone.NUMBER, "18004664422");
2698         mResolver.update(phoneUri, values, null, null);
2699         assertEquals(0, getCount(lookupUri1, null, null));
2700         assertEquals(2, getCount(lookupUri2, null, null));
2701         assertNetworkNotified(true);
2702     }
2703 
2704     /** Tests if {@link Callable#CONTENT_URI} returns both phones and sip addresses. */
testCallablesQuery()2705     public void testCallablesQuery() {
2706         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Meghan", "Knox");
2707         long phoneId1 = ContentUris.parseId(insertPhoneNumber(rawContactId1, "18004664411"));
2708         long contactId1 = queryContactId(rawContactId1);
2709 
2710         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe");
2711         long sipAddressId2 = ContentUris.parseId(
2712                 insertSipAddress(rawContactId2, "sip@example.com"));
2713         long contactId2 = queryContactId(rawContactId2);
2714 
2715         ContentValues values1 = new ContentValues();
2716         values1.put(Data._ID, phoneId1);
2717         values1.put(Data.RAW_CONTACT_ID, rawContactId1);
2718         values1.put(RawContacts.CONTACT_ID, contactId1);
2719         values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
2720         values1.put(Phone.NUMBER, "18004664411");
2721         values1.put(Phone.TYPE, Phone.TYPE_HOME);
2722         values1.putNull(Phone.LABEL);
2723         values1.put(Contacts.DISPLAY_NAME, "Meghan Knox");
2724 
2725         ContentValues values2 = new ContentValues();
2726         values2.put(Data._ID, sipAddressId2);
2727         values2.put(Data.RAW_CONTACT_ID, rawContactId2);
2728         values2.put(RawContacts.CONTACT_ID, contactId2);
2729         values2.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
2730         values2.put(SipAddress.SIP_ADDRESS, "sip@example.com");
2731         values2.put(Contacts.DISPLAY_NAME, "John Doe");
2732 
2733         assertEquals(2, getCount(Callable.CONTENT_URI, null, null));
2734         assertStoredValues(Callable.CONTENT_URI, new ContentValues[] { values1, values2 });
2735     }
2736 
testCallablesFilterQuery()2737     public void testCallablesFilterQuery() {
2738         testPhonesFilterQueryInter(Callable.CONTENT_FILTER_URI);
2739     }
2740 
testEmailsQuery()2741     public void testEmailsQuery() {
2742         ContentValues values = new ContentValues();
2743         values.put(RawContacts.CUSTOM_RINGTONE, "d");
2744         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
2745         values.put(RawContacts.LAST_TIME_CONTACTED, 86400 + 5);
2746         values.put(RawContacts.TIMES_CONTACTED, 54321);
2747         values.put(RawContacts.STARRED, 1);
2748 
2749         Uri rawContactUri = insertRawContact(values);
2750         final long rawContactId = ContentUris.parseId(rawContactUri);
2751 
2752         DataUtil.insertStructuredName(mResolver, rawContactId, "Meghan", "Knox");
2753         final Uri emailUri = insertEmail(rawContactId, "meghan@acme.com");
2754         final long emailId = ContentUris.parseId(emailUri);
2755 
2756         final long contactId = queryContactId(rawContactId);
2757         values.clear();
2758         values.put(Data._ID, emailId);
2759         values.put(Data.RAW_CONTACT_ID, rawContactId);
2760         values.put(RawContacts.CONTACT_ID, contactId);
2761         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
2762         values.put(Email.DATA, "meghan@acme.com");
2763         values.put(Email.TYPE, Email.TYPE_HOME);
2764         values.putNull(Email.LABEL);
2765         values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
2766         values.put(Contacts.CUSTOM_RINGTONE, "d");
2767         values.put(Contacts.SEND_TO_VOICEMAIL, 1);
2768         values.put(Contacts.LAST_TIME_CONTACTED, 0);
2769         values.put(Contacts.TIMES_CONTACTED, 0);
2770         values.put(Contacts.STARRED, 1);
2771 
2772         assertStoredValues(Email.CONTENT_URI, values);
2773         assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values);
2774 
2775         // Check if the provider detects duplicated email addresses.
2776         final Uri emailUri2 = insertEmail(rawContactId, "meghan@acme.com");
2777         final long emailId2 = ContentUris.parseId(emailUri2);
2778         final ContentValues values2 = new ContentValues(values);
2779         values2.put(Data._ID, emailId2);
2780 
2781         final Uri dedupeUri = Email.CONTENT_URI.buildUpon()
2782                 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
2783                 .build();
2784 
2785         // URI with ID should return a correct result.
2786         assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values);
2787         assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId), values);
2788         assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId2), values2);
2789         assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId2), values2);
2790 
2791         assertStoredValues(Email.CONTENT_URI, new ContentValues[] {values, values2});
2792 
2793         // If requested to remove duplicates, the query should return just one result,
2794         // whose _ID won't be deterministic.
2795         values.remove(Data._ID);
2796         assertStoredValues(dedupeUri, values);
2797     }
2798 
testEmailsLookupQuery()2799     public void testEmailsLookupQuery() {
2800         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale");
2801         insertEmail(rawContactId, "tamale@acme.com");
2802 
2803         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "tamale@acme.com");
2804         ContentValues values = new ContentValues();
2805         values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
2806         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
2807         values.put(Email.DATA, "tamale@acme.com");
2808         values.put(Email.TYPE, Email.TYPE_HOME);
2809         values.putNull(Email.LABEL);
2810         assertStoredValues(filterUri1, values);
2811 
2812         Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "Ta<TaMale@acme.com>");
2813         assertStoredValues(filterUri2, values);
2814 
2815         Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "encilada@acme.com");
2816         assertEquals(0, getCount(filterUri3, null, null));
2817     }
2818 
testEmailsFilterQuery()2819     public void testEmailsFilterQuery() {
2820         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale",
2821                 TestUtil.ACCOUNT_1);
2822         insertEmail(rawContactId1, "tamale@acme.com");
2823         insertEmail(rawContactId1, "tamale@acme.com");
2824 
2825         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale",
2826                 TestUtil.ACCOUNT_2);
2827         insertEmail(rawContactId2, "tamale@acme.com");
2828 
2829         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tam");
2830         ContentValues values = new ContentValues();
2831         values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
2832         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
2833         values.put(Email.DATA, "tamale@acme.com");
2834         values.put(Email.TYPE, Email.TYPE_HOME);
2835         values.putNull(Email.LABEL);
2836         assertStoredValuesWithProjection(filterUri1, values);
2837 
2838         Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot");
2839         assertStoredValuesWithProjection(filterUri2, values);
2840 
2841         Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot tamale");
2842         assertStoredValuesWithProjection(filterUri3, values);
2843 
2844         Uri filterUri4 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tamale@acme");
2845         assertStoredValuesWithProjection(filterUri4, values);
2846 
2847         Uri filterUri5 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "encilada");
2848         assertEquals(0, getCount(filterUri5, null, null));
2849     }
2850 
2851     /**
2852      * Tests if ContactsProvider2 returns addresses according to registration order.
2853      */
testEmailFilterDefaultSortOrder()2854     public void testEmailFilterDefaultSortOrder() {
2855         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
2856         insertEmail(rawContactId1, "address1@email.com");
2857         insertEmail(rawContactId1, "address2@email.com");
2858         insertEmail(rawContactId1, "address3@email.com");
2859         ContentValues v1 = new ContentValues();
2860         v1.put(Email.ADDRESS, "address1@email.com");
2861         ContentValues v2 = new ContentValues();
2862         v2.put(Email.ADDRESS, "address2@email.com");
2863         ContentValues v3 = new ContentValues();
2864         v3.put(Email.ADDRESS, "address3@email.com");
2865 
2866         Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
2867         assertStoredValuesOrderly(filterUri, new ContentValues[]{v1, v2, v3});
2868     }
2869 
2870     /**
2871      * Tests if ContactsProvider2 returns primary addresses before the other addresses.
2872      */
testEmailFilterPrimaryAddress()2873     public void testEmailFilterPrimaryAddress() {
2874         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
2875         insertEmail(rawContactId1, "address1@email.com");
2876         insertEmail(rawContactId1, "address2@email.com", true);
2877         ContentValues v1 = new ContentValues();
2878         v1.put(Email.ADDRESS, "address1@email.com");
2879         ContentValues v2 = new ContentValues();
2880         v2.put(Email.ADDRESS, "address2@email.com");
2881 
2882         Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
2883         assertStoredValuesOrderly(filterUri, new ContentValues[] { v2, v1 });
2884     }
2885 
2886     /**
2887      * Tests if ContactsProvider2 has email address associated with a primary account before the
2888      * other address.
2889      */
testEmailFilterPrimaryAccount()2890     public void testEmailFilterPrimaryAccount() {
2891         long rawContactId1 = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1);
2892         insertEmail(rawContactId1, "account1@email.com");
2893         long rawContactId2 = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_2);
2894         insertEmail(rawContactId2, "account2@email.com");
2895         ContentValues v1 = new ContentValues();
2896         v1.put(Email.ADDRESS, "account1@email.com");
2897         ContentValues v2 = new ContentValues();
2898         v2.put(Email.ADDRESS, "account2@email.com");
2899 
2900         Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
2901                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_1.name)
2902                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, TestUtil.ACCOUNT_1.type)
2903                 .build();
2904         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2 });
2905 
2906         Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
2907                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_2.name)
2908                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, TestUtil.ACCOUNT_2.type)
2909                 .build();
2910         assertStoredValuesOrderly(filterUri2, new ContentValues[] { v2, v1 });
2911 
2912         // Just with PRIMARY_ACCOUNT_NAME
2913         Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
2914                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_1.name)
2915                 .build();
2916         assertStoredValuesOrderly(filterUri3, new ContentValues[]{v1, v2});
2917 
2918         Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
2919                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_2.name)
2920                 .build();
2921         assertStoredValuesOrderly(filterUri4, new ContentValues[] { v2, v1 });
2922     }
2923 
2924     /**
2925      * Test emails with the same domain as primary account are ordered first.
2926      */
testEmailFilterSameDomainAccountOrder()2927     public void testEmailFilterSameDomainAccountOrder() {
2928         final Account account = new Account("tester@email.com", "not_used");
2929         final long rawContactId = RawContactUtil.createRawContact(mResolver, account);
2930         insertEmail(rawContactId, "account1@testemail.com");
2931         insertEmail(rawContactId, "account1@email.com");
2932 
2933         final ContentValues v1 = cv(Email.ADDRESS, "account1@testemail.com");
2934         final ContentValues v2 = cv(Email.ADDRESS, "account1@email.com");
2935 
2936         Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
2937                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, account.name)
2938                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, account.type)
2939                 .build();
2940         assertStoredValuesOrderly(filterUri1, v2, v1);
2941     }
2942 
2943     /**
2944      * Test "default" emails are sorted above emails used last.
2945      */
testEmailFilterSuperPrimaryOverUsageSort()2946     public void testEmailFilterSuperPrimaryOverUsageSort() {
2947         final long rawContactId = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1);
2948         final Uri emailUri1 = insertEmail(rawContactId, "account1@testemail.com");
2949         final Uri emailUri2 = insertEmail(rawContactId, "account2@testemail.com");
2950         insertEmail(rawContactId, "account3@testemail.com", true, true);
2951 
2952         // Update account1 and account 2 to have higher usage.
2953         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1);
2954         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1);
2955         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri2);
2956 
2957         final ContentValues v1 = cv(Email.ADDRESS, "account1@testemail.com");
2958         final ContentValues v2 = cv(Email.ADDRESS, "account2@testemail.com");
2959         final ContentValues v3 = cv(Email.ADDRESS, "account3@testemail.com");
2960 
2961         // Test that account 3 is first even though account 1 and 2 have higher usage.
2962         Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "acc");
2963         assertStoredValuesOrderly(filterUri, v3, v1, v2);
2964     }
2965 
testEmailFilterUsageOverPrimarySort()2966     public void testEmailFilterUsageOverPrimarySort() {
2967         final long rawContactId = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1);
2968         final Uri emailUri1 = insertEmail(rawContactId, "account1@testemail.com");
2969         final Uri emailUri2 = insertEmail(rawContactId, "account2@testemail.com");
2970         insertEmail(rawContactId, "account3@testemail.com", true);
2971 
2972         // Update account1 and account 2 to have higher usage.
2973         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1);
2974         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1);
2975         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri2);
2976 
2977         final ContentValues v1 = cv(Email.ADDRESS, "account1@testemail.com");
2978         final ContentValues v2 = cv(Email.ADDRESS, "account2@testemail.com");
2979         final ContentValues v3 = cv(Email.ADDRESS, "account3@testemail.com");
2980 
2981         // No usage stats any more, so v3 is still the first.
2982         Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "acc");
2983         assertStoredValuesOrderly(filterUri, v3, v1, v2);
2984     }
2985 
2986     /** Tests {@link DataUsageFeedback} correctly promotes a data row instead of a raw contact. */
testEmailFilterSortOrderWithFeedback()2987     public void testEmailFilterSortOrderWithFeedback() {
2988         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
2989         String address1 = "address1@email.com";
2990         insertEmail(rawContactId1, address1);
2991 
2992         long rawContactId2 = RawContactUtil.createRawContact(mResolver);
2993         String address2 = "address2@email.com";
2994         insertEmail(rawContactId2, address2);
2995         String address3 = "address3@email.com";
2996         ContentUris.parseId(insertEmail(rawContactId2, address3));
2997 
2998         ContentValues v1 = new ContentValues();
2999         v1.put(Email.ADDRESS, "address1@email.com");
3000         ContentValues v2 = new ContentValues();
3001         v2.put(Email.ADDRESS, "address2@email.com");
3002         ContentValues v3 = new ContentValues();
3003         v3.put(Email.ADDRESS, "address3@email.com");
3004 
3005         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
3006         Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
3007                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
3008                         DataUsageFeedback.USAGE_TYPE_CALL)
3009                 .build();
3010         Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
3011                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
3012                         DataUsageFeedback.USAGE_TYPE_LONG_TEXT)
3013                 .build();
3014         Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
3015                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
3016                         DataUsageFeedback.USAGE_TYPE_SHORT_TEXT)
3017                 .build();
3018         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2, v3 });
3019         assertStoredValuesOrderly(filterUri2, new ContentValues[] { v1, v2, v3 });
3020         assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2, v3 });
3021         assertStoredValuesOrderly(filterUri4, new ContentValues[] { v1, v2, v3 });
3022 
3023         sendFeedback(address3, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, v3);
3024 
3025         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
3026                 cv(RawContacts._ID, rawContactId1,
3027                         RawContacts.TIMES_CONTACTED, 0
3028                         ),
3029                 cv(RawContacts._ID, rawContactId2,
3030                         RawContacts.TIMES_CONTACTED, 0
3031                         )
3032                 );
3033 
3034         // No more interaction counter, so the order doesn't change.
3035         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2, v3 });
3036         assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2, v3 });
3037     }
3038 
testAddQueryParametersFromUri()3039     public void testAddQueryParametersFromUri() {
3040         final ContactsProvider2 provider = (ContactsProvider2) getProvider();
3041         final Uri originalUri = Phone.CONTENT_FILTER_URI.buildUpon()
3042                 .appendQueryParameter("a", "a")
3043                 .appendQueryParameter("b", "b")
3044                 .appendQueryParameter("c", "c").build();
3045         final Uri.Builder targetBuilder = Phone.CONTENT_FILTER_URI.buildUpon();
3046         provider.addQueryParametersFromUri(targetBuilder, originalUri,
3047                 new ArraySet<String>(Arrays.asList(new String[] {
3048                         "b"
3049                 })));
3050         final Uri targetUri = targetBuilder.build();
3051         assertEquals(1, targetUri.getQueryParameters("a").size());
3052         assertEquals(0, targetUri.getQueryParameters("b").size());
3053         assertEquals(1, targetUri.getQueryParameters("c").size());
3054     }
3055 
buildContactsFilterUriWithDirectory(String directory)3056     private Uri buildContactsFilterUriWithDirectory(String directory) {
3057         return Contacts.CONTENT_FILTER_URI.buildUpon()
3058                 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, directory).build();
3059     }
3060 
testTestInvalidDirectory()3061     public void testTestInvalidDirectory() throws Exception {
3062         final ContactsProvider2 provider = (ContactsProvider2) getProvider();
3063         assertTrue(provider.isDirectoryParamValid(Contacts.CONTENT_FILTER_URI));
3064         assertFalse(provider.isDirectoryParamValid(buildContactsFilterUriWithDirectory("")));
3065         assertTrue(provider.isDirectoryParamValid(buildContactsFilterUriWithDirectory("0")));
3066         assertTrue(provider.isDirectoryParamValid(buildContactsFilterUriWithDirectory("123")));
3067         assertFalse(provider.isDirectoryParamValid(buildContactsFilterUriWithDirectory("abc")));
3068     }
3069 
testQueryCorpContactsProvider()3070     public void testQueryCorpContactsProvider() throws Exception {
3071         final ContactsProvider2 provider = (ContactsProvider2) getProvider();
3072         final MockUserManager um = mActor.mockUserManager;
3073         final Uri enterpriseUri =
3074                 Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-222-2222");
3075         final Uri invalidAuthorityUri = android.provider.Settings.Secure.CONTENT_URI;
3076 
3077         // No corp user.  Primary only.
3078         assertEquals(-1, UserUtils.getCorpUserId(mActor.getProviderContext()));
3079         assertEquals(0, provider.queryCorpContactsProvider(enterpriseUri, null, null, null,
3080                 null, null).getCount());
3081 
3082         final SynchronousContactsProvider2 corpCp2 = setUpCorpProvider();
3083         // Insert a contact to the corp CP2
3084         long rawContactId = ContentUris.parseId(
3085                 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues()));
3086         // Insert a name
3087         ContentValues cv = cv(
3088                 Data.RAW_CONTACT_ID, rawContactId,
3089                 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE,
3090                 StructuredName.DISPLAY_NAME, "Contact2 Corp",
3091                 StructuredName.GIVEN_NAME, "Contact2",
3092                 StructuredName.FAMILY_NAME, "Corp");
3093         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
3094         // Insert a number
3095         cv = cv(
3096                 Data.RAW_CONTACT_ID, rawContactId,
3097                 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE,
3098                 Phone.NUMBER, "408-222-2222",
3099                 Phone.TYPE, Phone.TYPE_HOME);
3100         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
3101         // Primary + corp
3102         um.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.CORP_USER);
3103         // It returns 2 identical rows, probably because of the join in phone_lookup.
3104         assertEquals(2, provider.queryCorpContactsProvider(enterpriseUri, null, null, null,
3105                 null, null).getCount());
3106         try {
3107             provider.queryCorpContactsProvider(invalidAuthorityUri, null, null,
3108                     null, null, null);
3109             fail(invalidAuthorityUri.toString() + " should throw IllegalArgumentException");
3110         } catch (IllegalArgumentException e) {
3111             // Expected
3112         }
3113     }
3114 
testPostalsQuery()3115     public void testPostalsQuery() {
3116         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Alice", "Nextore");
3117         Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");
3118         final long dataId = ContentUris.parseId(dataUri);
3119 
3120         final long contactId = queryContactId(rawContactId);
3121         ContentValues values = new ContentValues();
3122         values.put(Data._ID, dataId);
3123         values.put(Data.RAW_CONTACT_ID, rawContactId);
3124         values.put(RawContacts.CONTACT_ID, contactId);
3125         values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
3126         values.put(StructuredPostal.FORMATTED_ADDRESS, "1600 Amphiteatre Ave, Mountain View");
3127         values.put(Contacts.DISPLAY_NAME, "Alice Nextore");
3128 
3129         assertStoredValues(StructuredPostal.CONTENT_URI, values);
3130         assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId),
3131                 values);
3132         assertSelection(StructuredPostal.CONTENT_URI, values, Data._ID, dataId);
3133 
3134         // Check if the provider detects duplicated addresses.
3135         Uri dataUri2 = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");
3136         final long dataId2 = ContentUris.parseId(dataUri2);
3137         final ContentValues values2 = new ContentValues(values);
3138         values2.put(Data._ID, dataId2);
3139 
3140         final Uri dedupeUri = StructuredPostal.CONTENT_URI.buildUpon()
3141                 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
3142                 .build();
3143 
3144         // URI with ID should return a correct result.
3145         assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId),
3146                 values);
3147         assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId), values);
3148         assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId2),
3149                 values2);
3150         assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId2), values2);
3151 
3152         assertStoredValues(StructuredPostal.CONTENT_URI, new ContentValues[] {values, values2});
3153 
3154         // If requested to remove duplicates, the query should return just one result,
3155         // whose _ID won't be deterministic.
3156         values.remove(Data._ID);
3157         assertStoredValues(dedupeUri, values);
3158     }
3159 
testDataContentUriInvisibleQuery()3160     public void testDataContentUriInvisibleQuery() {
3161         final ContentValues values = new ContentValues();
3162         final long contactId = createContact(values, "John", "Doe",
3163                 "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
3164                         StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
3165 
3166         final Uri uri = Data.CONTENT_URI.buildUpon().
3167                 appendQueryParameter(Data.VISIBLE_CONTACTS_ONLY, "true").build();
3168         assertEquals(4, getCount(uri, null, null));
3169 
3170         markInvisible(contactId);
3171 
3172         assertEquals(0, getCount(uri, null, null));
3173     }
3174 
testInDefaultDirectoryData()3175     public void testInDefaultDirectoryData() {
3176         final ContentValues values = new ContentValues();
3177         final long contactId = createContact(values, "John", "Doe",
3178                 "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
3179                 StatusUpdates.CAPABILITY_HAS_CAMERA);
3180 
3181         final StringBuilder query = new StringBuilder()
3182                 .append(Data.MIMETYPE).append("='").append(Email.CONTENT_ITEM_TYPE)
3183                 .append("' AND ").append(Email.DATA).append("=? AND ")
3184                 .append(Contacts.IN_DEFAULT_DIRECTORY).append("=1");
3185 
3186         assertEquals(1,
3187                 getCount(Email.CONTENT_URI, query.toString(), new String[]{"goog411@acme.com"}));
3188 
3189         // Fire!
3190         markInvisible(contactId);
3191 
3192         // Verify: making a contact visible changes the IN_DEFAULT_DIRECTORY data value.
3193         assertEquals(0,
3194                 getCount(Email.CONTENT_URI, query.toString(), new String[]{"goog411@acme.com"}));
3195     }
3196 
testContactablesQuery()3197     public void testContactablesQuery() {
3198         final long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot",
3199                 "Tamale");
3200 
3201         insertPhoneNumber(rawContactId, "510-123-5769");
3202         insertEmail(rawContactId, "tamale@acme.com");
3203 
3204         final ContentValues cv1 = new ContentValues();
3205         cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
3206         cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
3207         cv1.put(Email.DATA, "tamale@acme.com");
3208         cv1.put(Email.TYPE, Email.TYPE_HOME);
3209         cv1.putNull(Email.LABEL);
3210 
3211         final ContentValues cv2 = new ContentValues();
3212         cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale");
3213         cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
3214         cv2.put(Phone.DATA, "510-123-5769");
3215         cv2.put(Phone.TYPE, Phone.TYPE_HOME);
3216         cv2.putNull(Phone.LABEL);
3217 
3218         final Uri filterUri0 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "");
3219         assertEquals(0, getCount(filterUri0, null, null));
3220 
3221         final Uri filterUri1 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale");
3222         assertStoredValues(filterUri1, cv1, cv2);
3223 
3224         final Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "hot");
3225         assertStoredValues(filterUri2, cv1, cv2);
3226 
3227         final Uri filterUri3 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale@ac");
3228         assertStoredValues(filterUri3, cv1, cv2);
3229 
3230         final Uri filterUri4 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "510");
3231         assertStoredValues(filterUri4, cv1, cv2);
3232 
3233         final Uri filterUri5 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "cold");
3234         assertEquals(0, getCount(filterUri5, null, null));
3235 
3236         final Uri filterUri6 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI,
3237                 "tamale@google");
3238         assertEquals(0, getCount(filterUri6, null, null));
3239 
3240         final Uri filterUri7 = Contactables.CONTENT_URI;
3241         assertStoredValues(filterUri7, cv1, cv2);
3242     }
3243 
testContactablesMultipleQuery()3244     public void testContactablesMultipleQuery() {
3245 
3246         final long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot",
3247                 "Tamale");
3248         insertPhoneNumber(rawContactId, "510-123-5769");
3249         insertEmail(rawContactId, "tamale@acme.com");
3250         insertEmail(rawContactId, "hot@google.com");
3251 
3252         final long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Cold",
3253                 "Tamago");
3254         insertEmail(rawContactId2, "eggs@farmers.org");
3255 
3256         final long rawContactId3 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe");
3257         insertPhoneNumber(rawContactId3, "518-354-1111");
3258         insertEmail(rawContactId3, "doeadeer@afemaledeer.com");
3259 
3260         final ContentValues cv1 = new ContentValues();
3261         cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
3262         cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
3263         cv1.put(Email.DATA, "tamale@acme.com");
3264         cv1.put(Email.TYPE, Email.TYPE_HOME);
3265         cv1.putNull(Email.LABEL);
3266 
3267         final ContentValues cv2 = new ContentValues();
3268         cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale");
3269         cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
3270         cv2.put(Phone.DATA, "510-123-5769");
3271         cv2.put(Phone.TYPE, Phone.TYPE_HOME);
3272         cv2.putNull(Phone.LABEL);
3273 
3274         final ContentValues cv3 = new ContentValues();
3275         cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale");
3276         cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
3277         cv3.put(Email.DATA, "hot@google.com");
3278         cv3.put(Email.TYPE, Email.TYPE_HOME);
3279         cv3.putNull(Email.LABEL);
3280 
3281         final ContentValues cv4 = new ContentValues();
3282         cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago");
3283         cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
3284         cv4.put(Email.DATA, "eggs@farmers.org");
3285         cv4.put(Email.TYPE, Email.TYPE_HOME);
3286         cv4.putNull(Email.LABEL);
3287 
3288         final ContentValues cv5 = new ContentValues();
3289         cv5.put(Contacts.DISPLAY_NAME, "John Doe");
3290         cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
3291         cv5.put(Email.DATA, "doeadeer@afemaledeer.com");
3292         cv5.put(Email.TYPE, Email.TYPE_HOME);
3293         cv5.putNull(Email.LABEL);
3294 
3295         final ContentValues cv6 = new ContentValues();
3296         cv6.put(Contacts.DISPLAY_NAME, "John Doe");
3297         cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
3298         cv6.put(Phone.DATA, "518-354-1111");
3299         cv6.put(Phone.TYPE, Phone.TYPE_HOME);
3300         cv6.putNull(Phone.LABEL);
3301 
3302         final Uri filterUri1 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale");
3303 
3304         assertStoredValues(filterUri1, cv1, cv2, cv3);
3305 
3306         final Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "hot");
3307         assertStoredValues(filterUri2, cv1, cv2, cv3);
3308 
3309         final Uri filterUri3 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tam");
3310         assertStoredValues(filterUri3, cv1, cv2, cv3, cv4);
3311 
3312         final Uri filterUri4 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "518");
3313         assertStoredValues(filterUri4, cv5, cv6);
3314 
3315         final Uri filterUri5 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "doe");
3316         assertStoredValues(filterUri5, cv5, cv6);
3317 
3318         final Uri filterUri6 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "51");
3319         assertStoredValues(filterUri6, cv1, cv2, cv3, cv5, cv6);
3320 
3321         final Uri filterUri7 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI,
3322                 "tamale@google");
3323         assertEquals(0, getCount(filterUri7, null, null));
3324 
3325         final Uri filterUri8 = Contactables.CONTENT_URI;
3326         assertStoredValues(filterUri8, cv1, cv2, cv3, cv4, cv5, cv6);
3327 
3328         // test VISIBLE_CONTACTS_ONLY boolean parameter
3329         final Uri filterUri9 = filterUri6.buildUpon().appendQueryParameter(
3330                 Contactables.VISIBLE_CONTACTS_ONLY, "true").build();
3331         assertStoredValues(filterUri9, cv1, cv2, cv3, cv5, cv6);
3332         // mark Hot Tamale as invisible - cv1, cv2, and cv3 should no longer be in the cursor
3333         markInvisible(queryContactId(rawContactId));
3334         assertStoredValues(filterUri9, cv5, cv6);
3335     }
3336 
3337 
testQueryContactData()3338     public void testQueryContactData() {
3339         ContentValues values = new ContentValues();
3340         long contactId = createContact(values, "John", "Doe",
3341                 "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
3342                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
3343         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
3344 
3345         values.put(Contacts.TIMES_CONTACTED, 0);
3346         assertStoredValues(contactUri, values);
3347     }
3348 
testQueryContactWithStatusUpdate()3349     public void testQueryContactWithStatusUpdate() {
3350         ContentValues values = new ContentValues();
3351         long contactId = createContact(values, "John", "Doe",
3352                 "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
3353                 StatusUpdates.CAPABILITY_HAS_CAMERA);
3354         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
3355         values.put(Contacts.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
3356         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
3357 
3358         values.put(Contacts.TIMES_CONTACTED, 0);
3359 
3360         assertStoredValuesWithProjection(contactUri, values);
3361     }
3362 
testQueryContactFilterByName()3363     public void testQueryContactFilterByName() {
3364         ContentValues values = new ContentValues();
3365         long rawContactId = createRawContact(values, "18004664411",
3366                 "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
3367                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
3368                 StatusUpdates.CAPABILITY_HAS_VOICE);
3369 
3370         ContentValues nameValues = new ContentValues();
3371         nameValues.put(StructuredName.GIVEN_NAME, "Stu");
3372         nameValues.put(StructuredName.FAMILY_NAME, "Goulash");
3373         nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "goo");
3374         nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "LASH");
3375         Uri nameUri = DataUtil.insertStructuredName(mResolver, rawContactId, nameValues);
3376 
3377         long contactId = queryContactId(rawContactId);
3378         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
3379 
3380         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goulash");
3381         values.put(Contacts.TIMES_CONTACTED, 0);
3382         assertStoredValuesWithProjection(filterUri1, values);
3383 
3384         assertContactFilter(contactId, "goolash");
3385         assertContactFilter(contactId, "lash");
3386 
3387         assertContactFilterNoResult("goolish");
3388 
3389         // Phonetic name with given/family reversed should not match
3390         assertContactFilterNoResult("lashgoo");
3391 
3392         nameValues.clear();
3393         nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "ga");
3394         nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "losh");
3395 
3396         mResolver.update(nameUri, nameValues, null, null);
3397 
3398         assertContactFilter(contactId, "galosh");
3399 
3400         assertContactFilterNoResult("goolish");
3401     }
3402 
testQueryContactFilterByEmailAddress()3403     public void testQueryContactFilterByEmailAddress() {
3404         ContentValues values = new ContentValues();
3405         long rawContactId = createRawContact(values, "18004664411",
3406                 "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
3407                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
3408                 StatusUpdates.CAPABILITY_HAS_VOICE);
3409 
3410         DataUtil.insertStructuredName(mResolver, rawContactId, "James", "Bond");
3411 
3412         long contactId = queryContactId(rawContactId);
3413         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
3414 
3415         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goog411@acme.com");
3416         values.put(Contacts.TIMES_CONTACTED, 0);
3417         assertStoredValuesWithProjection(filterUri1, values);
3418 
3419         assertContactFilter(contactId, "goog");
3420         assertContactFilter(contactId, "goog411");
3421         assertContactFilter(contactId, "goog411@");
3422         assertContactFilter(contactId, "goog411@acme");
3423         assertContactFilter(contactId, "goog411@acme.com");
3424 
3425         assertContactFilterNoResult("goog411@acme.combo");
3426         assertContactFilterNoResult("goog411@le.com");
3427         assertContactFilterNoResult("goolish");
3428     }
3429 
testQueryContactFilterByPhoneNumber()3430     public void testQueryContactFilterByPhoneNumber() {
3431         ContentValues values = new ContentValues();
3432         long rawContactId = createRawContact(values, "18004664411",
3433                 "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
3434                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
3435                 StatusUpdates.CAPABILITY_HAS_VOICE);
3436 
3437         DataUtil.insertStructuredName(mResolver, rawContactId, "James", "Bond");
3438 
3439         long contactId = queryContactId(rawContactId);
3440         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
3441 
3442         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "18004664411");
3443         values.put(Contacts.TIMES_CONTACTED, 0);
3444         assertStoredValuesWithProjection(filterUri1, values);
3445 
3446         assertContactFilter(contactId, "18004664411");
3447         assertContactFilter(contactId, "1800466");
3448         assertContactFilter(contactId, "+18004664411");
3449         assertContactFilter(contactId, "8004664411");
3450 
3451         assertContactFilterNoResult("78004664411");
3452         assertContactFilterNoResult("18004664412");
3453         assertContactFilterNoResult("8884664411");
3454     }
3455 
3456     /**
3457      * Checks ContactsProvider2 works well with strequent Uris. The provider should return starred
3458      * contacts.
3459      */
testQueryContactStrequent()3460     public void testQueryContactStrequent() {
3461         ContentValues values1 = new ContentValues();
3462         final String email1 = "a@acme.com";
3463         final String phoneNumber1 = "18004664411";
3464         final int timesContacted1 = 0;
3465         createContact(values1, "Noah", "Tever", phoneNumber1,
3466                 email1, StatusUpdates.OFFLINE, timesContacted1, 0, 0,
3467                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
3468         final String phoneNumber2 = "18004664412";
3469         ContentValues values2 = new ContentValues();
3470         createContact(values2, "Sam", "Times", phoneNumber2,
3471                 "b@acme.com", StatusUpdates.INVISIBLE, 3, 0, 0,
3472                 StatusUpdates.CAPABILITY_HAS_CAMERA);
3473         ContentValues values3 = new ContentValues();
3474         final String phoneNumber3 = "18004664413";
3475         final int timesContacted3 = 9;
3476         createContact(values3, "Lotta", "Calling", phoneNumber3,
3477                 "c@acme.com", StatusUpdates.AWAY, timesContacted3, 0, 0,
3478                 StatusUpdates.CAPABILITY_HAS_VIDEO);
3479         ContentValues values4 = new ContentValues();
3480         final long rawContactId4 = createRawContact(values4, "Fay", "Veritt", null,
3481                 "d@acme.com", StatusUpdates.AVAILABLE, 0, 1, 0,
3482                 StatusUpdates.CAPABILITY_HAS_VIDEO | StatusUpdates.CAPABILITY_HAS_VOICE);
3483 
3484         // Starred contacts should be returned. TIMES_CONTACTED should be ignored and only data
3485         // usage feedback should be used for "frequently contacted" listing.
3486         assertStoredValues(Contacts.CONTENT_STREQUENT_URI, values4);
3487 
3488         // Send feedback for the 3rd phone number, pretending we called that person via phone.
3489         sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
3490 
3491         values3.put(Contacts.TIMES_CONTACTED, 0);
3492 
3493         // After the feedback, 3rd contact should be shown after starred one.
3494         assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI,
3495                 new ContentValues[] { values4 });
3496 
3497         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
3498         // Twice.
3499         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
3500 
3501         // After the feedback, 1st and 3rd contacts should be shown after starred one.
3502         values1.put(Contacts.TIMES_CONTACTED, 0);
3503         assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI,
3504                 new ContentValues[] { values4 });
3505 
3506         // With phone-only parameter, 1st and 4th contacts shouldn't be returned because:
3507         // 1st: feedbacks are only about email, not about phone call.
3508         // 4th: it has no phone number though starred.
3509         Uri phoneOnlyStrequentUri = Contacts.CONTENT_STREQUENT_URI.buildUpon()
3510                 .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true")
3511                 .build();
3512         assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { });
3513     }
3514 
testQueryContactStrequentFrequentOrder()3515     public void testQueryContactStrequentFrequentOrder() {
3516         // Prepare test data
3517         final long rid1 = RawContactUtil.createRawContact(mResolver);
3518         final long did1 = ContentUris.parseId(insertPhoneNumber(rid1, "1"));
3519         final long did1e = ContentUris.parseId(insertEmail(rid1, "1@email.com"));
3520 
3521         final long rid2 = RawContactUtil.createRawContact(mResolver);
3522         final long did2 = ContentUris.parseId(insertPhoneNumber(rid2, "2"));
3523 
3524         final long rid3 = RawContactUtil.createRawContact(mResolver);
3525         final long did3 = ContentUris.parseId(insertPhoneNumber(rid3, "3"));
3526 
3527         final long rid4 = RawContactUtil.createRawContact(mResolver);
3528         final long did4 = ContentUris.parseId(insertPhoneNumber(rid4, "4"));
3529 
3530         final long rid5 = RawContactUtil.createRawContact(mResolver);
3531         final long did5 = ContentUris.parseId(insertPhoneNumber(rid5, "5"));
3532 
3533         final long rid6 = RawContactUtil.createRawContact(mResolver);
3534         final long did6 = ContentUris.parseId(insertPhoneNumber(rid6, "6"));
3535 
3536         final long rid7 = RawContactUtil.createRawContact(mResolver);
3537         final long did7 = ContentUris.parseId(insertPhoneNumber(rid7, "7"));
3538 
3539         final long rid8 = RawContactUtil.createRawContact(mResolver);
3540         final long did8 = ContentUris.parseId(insertPhoneNumber(rid8, "8"));
3541 
3542         final long cid1 = queryContactId(rid1);
3543         final long cid2 = queryContactId(rid2);
3544         final long cid3 = queryContactId(rid3);
3545         final long cid4 = queryContactId(rid4);
3546         final long cid5 = queryContactId(rid5);
3547         final long cid6 = queryContactId(rid6);
3548         final long cid7 = queryContactId(rid7);
3549         final long cid8 = queryContactId(rid8);
3550 
3551         // Make sure they aren't aggregated.
3552         EvenMoreAsserts.assertUnique(cid1, cid2, cid3, cid4, cid5, cid6, cid7, cid8);
3553 
3554         // Prepare the clock
3555         sMockClock.install();
3556 
3557         // We check the timestamp in SQL, which doesn't know about the MockClock.  So we need to
3558         // use the  actual (roughly) time.
3559 
3560         final long nowInMillis = System.currentTimeMillis();
3561         final long oneDayAgoInMillis = (nowInMillis - 24L * 60 * 60 * 1000);
3562         final long fourDaysAgoInMillis = (nowInMillis - 4L * 24 * 60 * 60 * 1000);
3563         final long eightDaysAgoInMillis = (nowInMillis - 8L * 24 * 60 * 60 * 1000);
3564         final long fifteenDaysAgoInMillis = (nowInMillis - 15L * 24 * 60 * 60 * 1000);
3565         // All contacts older than 30 days will not be included in frequents
3566         final long thirtyOneDaysAgoInMillis = (nowInMillis - 31L * 24 * 60 * 60 * 1000);
3567 
3568         // Contacts in this bucket are considered more than 30 days old
3569         sMockClock.setCurrentTimeMillis(thirtyOneDaysAgoInMillis);
3570 
3571         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did1, did2);
3572         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did1);
3573 
3574         // Contacts in this bucket are considered more than 14 days old
3575         sMockClock.setCurrentTimeMillis(fifteenDaysAgoInMillis);
3576 
3577         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did3, did4);
3578         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did3);
3579 
3580         // Contacts in this bucket are considered more than 7 days old
3581         sMockClock.setCurrentTimeMillis(eightDaysAgoInMillis);
3582 
3583         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did5, did6);
3584         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did5);
3585 
3586         // Contact cid1 again, but it's an email, not a phone call.
3587         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1e);
3588         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1e);
3589         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1e);
3590 
3591         // Contacts in this bucket are considered more than 3 days old
3592         sMockClock.setCurrentTimeMillis(fourDaysAgoInMillis);
3593 
3594         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did7);
3595         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did7);
3596 
3597 
3598         // Contacts in this bucket are considered less than 3 days old
3599         sMockClock.setCurrentTimeMillis(oneDayAgoInMillis);
3600 
3601         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did8);
3602 
3603         sMockClock.setCurrentTimeMillis(nowInMillis);
3604 
3605         // Check the order -- The regular frequent, which is contact based.
3606         // Note because we contacted cid1 8 days ago, it's been contacted 3 times, so it comes
3607         // before cid5 and cid6, which were contacted at the same time.
3608         // cid2 will not show up because it was contacted more than 30 days ago
3609 
3610         assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI);
3611 
3612         // Check the order -- phone only frequent, which is data based.
3613         // Note this is based on data, and only looks at phone numbers, so the order is different
3614         // now.
3615         // did1, did2 will not show up because they were used to make calls more than 30 days ago.
3616         assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI.buildUpon()
3617                     .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "1").build());
3618     }
3619 
3620     /**
3621      * Checks ContactsProvider2 works well with frequent Uri. The provider should return frequently
3622      * contacted person ordered by number of times contacted.
3623      */
testQueryContactFrequent()3624     public void testQueryContactFrequent() {
3625         ContentValues values1 = new ContentValues();
3626         final String email1 = "a@acme.com";
3627         createContact(values1, "Noah", "Tever", "18004664411",
3628                 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0);
3629         ContentValues values2 = new ContentValues();
3630         final String email2 = "b@acme.com";
3631         createContact(values2, "Sam", "Times", "18004664412",
3632                 email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0);
3633         ContentValues values3 = new ContentValues();
3634         final String phoneNumber3 = "18004664413";
3635         final long contactId3 = createContact(values3, "Lotta", "Calling", phoneNumber3,
3636                 "c@acme.com", StatusUpdates.AWAY, 0, 1, 0, 0);
3637         ContentValues values4 = new ContentValues();
3638         createContact(values4, "Fay", "Veritt", "18004664414",
3639                 "d@acme.com", StatusUpdates.AVAILABLE, 0, 1, 0, 0);
3640 
3641         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
3642 
3643         assertStoredValues(Contacts.CONTENT_FREQUENT_URI);
3644 
3645         // Pretend email was sent to the address twice.
3646         sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2);
3647         sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2);
3648 
3649         values1.put(Contacts.TIMES_CONTACTED, 0);
3650         values2.put(Contacts.TIMES_CONTACTED, 0);
3651         assertStoredValues(Contacts.CONTENT_FREQUENT_URI);
3652 
3653         for (int i = 0; i < 10; i++) {
3654             sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
3655         }
3656 
3657         values3.put(Contacts.TIMES_CONTACTED, 0);
3658 
3659         assertStoredValues(Contacts.CONTENT_FREQUENT_URI);
3660 
3661 
3662         // Test it works with selection/selectionArgs
3663         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
3664                 Contacts.STARRED + "=?", new String[] {"0"}
3665                 );
3666         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
3667                 Contacts.STARRED + "=?", new String[] {"1"}
3668                 );
3669 
3670         values3.put(Contacts.STARRED, 0);
3671         assertEquals(1,
3672                 mResolver.update(Uri.withAppendedPath(Contacts.CONTENT_URI,
3673                         String.valueOf(contactId3)),
3674                 values3, null, null));
3675         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
3676                 Contacts.STARRED + "=?", new String[] {"0"}
3677                 );
3678         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
3679                 Contacts.STARRED + "=?", new String[] {"1"}
3680                 );
3681     }
3682 
testQueryContactFrequentExcludingInvisible()3683     public void testQueryContactFrequentExcludingInvisible() {
3684         ContentValues values1 = new ContentValues();
3685         final String email1 = "a@acme.com";
3686         final long cid1 = createContact(values1, "Noah", "Tever", "18004664411",
3687                 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0);
3688         ContentValues values2 = new ContentValues();
3689         final String email2 = "b@acme.com";
3690         final long cid2 = createContact(values2, "Sam", "Times", "18004664412",
3691                 email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0);
3692 
3693         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
3694         sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2);
3695 
3696         // First, we have two contacts in frequent.
3697         assertStoredValues(Contacts.CONTENT_FREQUENT_URI);
3698 
3699         // Contact 2 goes invisible.
3700         markInvisible(cid2);
3701 
3702         // Now we have only 1 frequent.
3703         assertStoredValues(Contacts.CONTENT_FREQUENT_URI);
3704 
3705     }
3706 
testQueryDataUsageStat()3707     public void testQueryDataUsageStat() {
3708         // Now all data usage stats are zero as of Q.
3709 
3710         ContentValues values1 = new ContentValues();
3711         final String email1 = "a@acme.com";
3712         final long cid1 = createContact(values1, "Noah", "Tever", "18004664411",
3713                 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0);
3714 
3715         sMockClock.install();
3716         sMockClock.setCurrentTimeMillis(100);
3717 
3718         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
3719 
3720         assertDataUsageZero(Data.CONTENT_URI, "a@acme.com");
3721 
3722         sMockClock.setCurrentTimeMillis(86400 + 123);
3723         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
3724 
3725         assertDataUsageZero(Data.CONTENT_URI, "a@acme.com");
3726 
3727         sMockClock.setCurrentTimeMillis(86400 * 3 + 123);
3728         for (int i = 0; i < 11; i++) {
3729             sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1);
3730         }
3731 
3732         assertDataUsageZero(Data.CONTENT_URI, "a@acme.com");
3733 
3734         final Uri dataUriWithUsageTypeLongText = Data.CONTENT_URI.buildUpon().appendQueryParameter(
3735                 DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_LONG_TEXT).build();
3736 
3737         assertDataUsageZero(dataUriWithUsageTypeLongText, "a@acme.com");
3738 
3739         sMockClock.setCurrentTimeMillis(86400 * 4 + 123);
3740         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
3741         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
3742         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
3743 
3744         assertDataUsageZero(Data.CONTENT_URI, "a@acme.com");
3745 
3746         sMockClock.setCurrentTimeMillis(86400 * 5 + 123);
3747         for (int i = 0; i < 10; i++) {
3748             sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
3749         }
3750         assertDataUsageZero(Data.CONTENT_URI, "a@acme.com");
3751 
3752         sMockClock.setCurrentTimeMillis(86400 * 6 + 123);
3753         for (int i = 0; i < 10; i++) {
3754             sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
3755         }
3756         assertDataUsageZero(Data.CONTENT_URI, "a@acme.com");
3757 
3758         final Uri dataUriWithUsageTypeCall = Data.CONTENT_URI.buildUpon().appendQueryParameter(
3759                 DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_CALL).build();
3760 
3761         assertDataUsageZero(dataUriWithUsageTypeCall, "a@acme.com");
3762     }
3763 
testQueryContactGroup()3764     public void testQueryContactGroup() {
3765         long groupId = createGroup(null, "testGroup", "Test Group");
3766 
3767         ContentValues values1 = new ContentValues();
3768         createContact(values1, "Best", "West", "18004664411",
3769                 "west@acme.com", StatusUpdates.OFFLINE, 0, 0, groupId,
3770                 StatusUpdates.CAPABILITY_HAS_CAMERA);
3771 
3772         ContentValues values2 = new ContentValues();
3773         createContact(values2, "Rest", "East", "18004664422",
3774                 "east@acme.com", StatusUpdates.AVAILABLE, 0, 0, 0,
3775                 StatusUpdates.CAPABILITY_HAS_VOICE);
3776 
3777         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
3778         Cursor c = mResolver.query(filterUri1, null, null, null, Contacts._ID);
3779         assertEquals(1, c.getCount());
3780         c.moveToFirst();
3781         dumpCursor(c);
3782         assertCursorValues(c, values1);
3783         c.close();
3784 
3785         Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
3786         c = mResolver.query(filterUri2, null, Contacts.DISPLAY_NAME + "=?",
3787                 new String[] { "Best West" }, Contacts._ID);
3788         assertEquals(1, c.getCount());
3789         c.close();
3790 
3791         Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Next Group");
3792         c = mResolver.query(filterUri3, null, null, null, Contacts._ID);
3793         assertEquals(0, c.getCount());
3794         c.close();
3795     }
3796 
expectNoSecurityException(String failureMessage, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)3797     private void expectNoSecurityException(String failureMessage, Uri uri, String[] projection,
3798             String selection, String[] selectionArgs, String sortOrder) {
3799         Cursor c = null;
3800         try {
3801             c = mResolver.query(uri, projection, selection, selectionArgs, sortOrder);
3802         } catch (SecurityException expected) {
3803             fail(failureMessage);
3804         } finally {
3805             if (c != null) {
3806                 c.close();
3807             }
3808         }
3809     }
3810 
expectSecurityException(String failureMessage, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)3811     private void expectSecurityException(String failureMessage, Uri uri, String[] projection,
3812             String selection, String[] selectionArgs, String sortOrder) {
3813         Cursor c = null;
3814         try {
3815             c = mResolver.query(uri, projection, selection, selectionArgs, sortOrder);
3816             fail(failureMessage);
3817         } catch (SecurityException expected) {
3818             // The security exception is expected to occur because we're missing a permission.
3819         } finally {
3820             if (c != null) {
3821                 c.close();
3822             }
3823         }
3824     }
3825 
testQueryProfileWithoutPermission()3826     public void testQueryProfileWithoutPermission() {
3827         createBasicProfileContact(new ContentValues());
3828 
3829         // Case 1: Retrieving profile contact.
3830         expectNoSecurityException(
3831                 "Querying for the profile without READ_PROFILE access should succeed.",
3832                 Profile.CONTENT_URI, null, null, null, Contacts._ID);
3833 
3834         // Case 2: Retrieving profile data.
3835         expectNoSecurityException(
3836                 "Querying for the profile data without READ_PROFILE access should succeed.",
3837                 Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
3838                 null, null, null, Contacts._ID);
3839 
3840         // Case 3: Retrieving profile entities.
3841         expectNoSecurityException(
3842                 "Querying for the profile entities without READ_PROFILE access should succeed.",
3843                 Profile.CONTENT_URI.buildUpon()
3844                         .appendPath("entities").build(), null, null, null, Contacts._ID);
3845     }
3846 
testQueryProfileByContactIdWithoutReadPermission()3847     public void testQueryProfileByContactIdWithoutReadPermission() {
3848         long profileRawContactId = createBasicProfileContact(new ContentValues());
3849         long profileContactId = queryContactId(profileRawContactId);
3850 
3851         // A query for the profile contact by ID should not require READ_PROFILE.
3852         expectNoSecurityException(
3853                 "Querying for the profile by contact ID without READ_PROFILE access should succeed",
3854                 ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId),
3855                 null, null, null, Contacts._ID);
3856     }
3857 
testQueryProfileByRawContactIdWithoutReadPermission()3858     public void testQueryProfileByRawContactIdWithoutReadPermission() {
3859         long profileRawContactId = createBasicProfileContact(new ContentValues());
3860 
3861         expectNoSecurityException(
3862                 "Querying for the raw contact profile without READ_PROFILE access should succeed.",
3863                 ContentUris.withAppendedId(RawContacts.CONTENT_URI,
3864                         profileRawContactId), null, null, null, RawContacts._ID);
3865     }
3866 
testQueryProfileRawContactWithoutReadPermission()3867     public void testQueryProfileRawContactWithoutReadPermission() {
3868         long profileRawContactId = createBasicProfileContact(new ContentValues());
3869 
3870         // Case 1: Retrieve the overall raw contact set for the profile.
3871         expectNoSecurityException(
3872                 "Querying for the raw contact profile without READ_PROFILE access should succeed.",
3873                 Profile.CONTENT_RAW_CONTACTS_URI, null, null, null, null);
3874 
3875         // Case 2: Retrieve the raw contact profile data for the inserted raw contact ID.
3876         expectNoSecurityException(
3877                 "Querying for the raw profile data without READ_PROFILE access should succeed.",
3878                 ContentUris.withAppendedId(
3879                         Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
3880                         .appendPath("data").build(), null, null, null, null);
3881 
3882         // Case 3: Retrieve the raw contact profile entity for the inserted raw contact ID.
3883         expectNoSecurityException(
3884                 "Querying for the raw profile entities without READ_PROFILE access should succeed.",
3885                 ContentUris.withAppendedId(
3886                         Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
3887                         .appendPath("entity").build(), null, null, null, null);
3888     }
3889 
testQueryProfileDataByDataIdWithoutReadPermission()3890     public void testQueryProfileDataByDataIdWithoutReadPermission() {
3891         createBasicProfileContact(new ContentValues());
3892         Cursor c = mResolver.query(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
3893                 new String[]{Data._ID, Data.MIMETYPE}, null, null, null);
3894         assertEquals(4, c.getCount());  // Photo, phone, email, name.
3895         c.moveToFirst();
3896         long profileDataId = c.getLong(0);
3897         c.close();
3898 
3899         expectNoSecurityException(
3900                 "Querying for the data in the profile without READ_PROFILE access should succeed.",
3901                 ContentUris.withAppendedId(Data.CONTENT_URI, profileDataId),
3902                 null, null, null, null);
3903     }
3904 
testQueryProfileDataWithoutReadPermission()3905     public void testQueryProfileDataWithoutReadPermission() {
3906         createBasicProfileContact(new ContentValues());
3907 
3908         expectNoSecurityException(
3909                 "Querying for the data in the profile without READ_PROFILE access should succeed.",
3910                 Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
3911                 null, null, null, null);
3912     }
3913 
testInsertProfileWithoutWritePermission()3914     public void testInsertProfileWithoutWritePermission() {
3915         // Creating a non-profile contact should be fine.
3916         createBasicNonProfileContact(new ContentValues());
3917 
3918         try {
3919             createBasicProfileContact(new ContentValues());
3920         } catch (SecurityException expected) {
3921             fail("Creating a profile contact should not require WRITE_PROFILE access.");
3922         }
3923     }
3924 
testInsertProfileDataWithoutWritePermission()3925     public void testInsertProfileDataWithoutWritePermission() {
3926         long profileRawContactId = createBasicProfileContact(new ContentValues());
3927 
3928         try {
3929             insertEmail(profileRawContactId, "foo@bar.net", false);
3930         } catch (SecurityException expected) {
3931             fail("Inserting data into a profile contact should not require WRITE_PROFILE access.");
3932         }
3933     }
3934 
testUpdateDataDoesNotRequireProfilePermission()3935     public void testUpdateDataDoesNotRequireProfilePermission() {
3936         // Create a non-profile contact.
3937         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Domo", "Arigato");
3938         long dataId = getStoredLongValue(Data.CONTENT_URI,
3939                 Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
3940                 new String[]{String.valueOf(rawContactId), StructuredName.CONTENT_ITEM_TYPE},
3941                 Data._ID);
3942 
3943         // Updates its name using a selection.
3944         ContentValues values = new ContentValues();
3945         values.put(StructuredName.GIVEN_NAME, "Bob");
3946         values.put(StructuredName.FAMILY_NAME, "Blob");
3947         mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?",
3948                 new String[]{String.valueOf(dataId)});
3949 
3950         // Check that the update went through.
3951         assertStoredValues(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), values);
3952     }
3953 
testQueryContactThenProfile()3954     public void testQueryContactThenProfile() {
3955         ContentValues profileValues = new ContentValues();
3956         long profileRawContactId = createBasicProfileContact(profileValues);
3957         long profileContactId = queryContactId(profileRawContactId);
3958 
3959         ContentValues nonProfileValues = new ContentValues();
3960         long nonProfileRawContactId = createBasicNonProfileContact(nonProfileValues);
3961         long nonProfileContactId = queryContactId(nonProfileRawContactId);
3962 
3963         nonProfileValues.put(Contacts.TIMES_CONTACTED, 0);
3964         profileValues.put(Contacts.TIMES_CONTACTED, 0);
3965 
3966         assertStoredValues(Contacts.CONTENT_URI, nonProfileValues);
3967 
3968         assertStoredValues(Profile.CONTENT_URI, profileValues);
3969     }
3970 
testQueryContactExcludeProfile()3971     public void testQueryContactExcludeProfile() {
3972         // Create a profile contact (it should not be returned by the general contact URI).
3973         createBasicProfileContact(new ContentValues());
3974 
3975         // Create a non-profile contact - this should be returned.
3976         ContentValues nonProfileValues = new ContentValues();
3977         createBasicNonProfileContact(nonProfileValues);
3978         nonProfileValues.put(Contacts.TIMES_CONTACTED, 0);
3979         assertStoredValues(Contacts.CONTENT_URI, new ContentValues[] {nonProfileValues});
3980     }
3981 
testQueryProfile()3982     public void testQueryProfile() {
3983         ContentValues profileValues = new ContentValues();
3984         createBasicProfileContact(profileValues);
3985 
3986         profileValues.put(Contacts.TIMES_CONTACTED, 0);
3987         assertStoredValues(Profile.CONTENT_URI, profileValues);
3988     }
3989 
getExpectedProfileDataValues()3990     private ContentValues[] getExpectedProfileDataValues() {
3991         // Expected photo data values (only field is the photo BLOB, which we can't check).
3992         ContentValues photoRow = new ContentValues();
3993         photoRow.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
3994 
3995         // Expected phone data values.
3996         ContentValues phoneRow = new ContentValues();
3997         phoneRow.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
3998         phoneRow.put(Phone.NUMBER, "18005554411");
3999 
4000         // Expected email data values.
4001         ContentValues emailRow = new ContentValues();
4002         emailRow.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
4003         emailRow.put(Email.ADDRESS, "mia.prophyl@acme.com");
4004 
4005         // Expected name data values.
4006         ContentValues nameRow = new ContentValues();
4007         nameRow.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
4008         nameRow.put(StructuredName.DISPLAY_NAME, "Mia Prophyl");
4009         nameRow.put(StructuredName.GIVEN_NAME, "Mia");
4010         nameRow.put(StructuredName.FAMILY_NAME, "Prophyl");
4011 
4012         return new ContentValues[]{photoRow, phoneRow, emailRow, nameRow};
4013     }
4014 
testQueryProfileData()4015     public void testQueryProfileData() {
4016         createBasicProfileContact(new ContentValues());
4017 
4018         assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
4019                 getExpectedProfileDataValues());
4020     }
4021 
testQueryProfileEntities()4022     public void testQueryProfileEntities() {
4023         createBasicProfileContact(new ContentValues());
4024 
4025         assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("entities").build(),
4026                 getExpectedProfileDataValues());
4027     }
4028 
testQueryRawProfile()4029     public void testQueryRawProfile() {
4030         ContentValues profileValues = new ContentValues();
4031         createBasicProfileContact(profileValues);
4032 
4033         // The raw contact view doesn't include the photo ID.
4034         profileValues.remove(Contacts.PHOTO_ID);
4035         profileValues.put(Contacts.TIMES_CONTACTED, 0);
4036         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, profileValues);
4037     }
4038 
testQueryRawProfileById()4039     public void testQueryRawProfileById() {
4040         ContentValues profileValues = new ContentValues();
4041         long profileRawContactId = createBasicProfileContact(profileValues);
4042 
4043         // The raw contact view doesn't include the photo ID.
4044         profileValues.remove(Contacts.PHOTO_ID);
4045         profileValues.put(Contacts.TIMES_CONTACTED, 0);
4046         assertStoredValues(ContentUris.withAppendedId(
4047                 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId), profileValues);
4048     }
4049 
testQueryRawProfileData()4050     public void testQueryRawProfileData() {
4051         long profileRawContactId = createBasicProfileContact(new ContentValues());
4052 
4053         assertStoredValues(ContentUris.withAppendedId(
4054                 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
4055                 .appendPath("data").build(), getExpectedProfileDataValues());
4056     }
4057 
testQueryRawProfileEntity()4058     public void testQueryRawProfileEntity() {
4059         long profileRawContactId = createBasicProfileContact(new ContentValues());
4060 
4061         assertStoredValues(ContentUris.withAppendedId(
4062                 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
4063                 .appendPath("entity").build(), getExpectedProfileDataValues());
4064     }
4065 
testQueryDataForProfile()4066     public void testQueryDataForProfile() {
4067         createBasicProfileContact(new ContentValues());
4068 
4069         assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
4070                 getExpectedProfileDataValues());
4071     }
4072 
testUpdateProfileRawContact()4073     public void testUpdateProfileRawContact() {
4074         createBasicProfileContact(new ContentValues());
4075         ContentValues updatedValues = new ContentValues();
4076         updatedValues.put(RawContacts.SEND_TO_VOICEMAIL, 0);
4077         updatedValues.put(RawContacts.CUSTOM_RINGTONE, "rachmaninoff3");
4078         updatedValues.put(RawContacts.STARRED, 1);
4079         mResolver.update(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues, null, null);
4080 
4081         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues);
4082     }
4083 
testInsertProfileWithDataSetTriggersAccountCreation()4084     public void testInsertProfileWithDataSetTriggersAccountCreation() {
4085         // Check that we have no profile raw contacts.
4086         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, new ContentValues[]{});
4087 
4088         // Insert a profile record with a new data set.
4089         Account account = new Account("a", "b");
4090         String dataSet = "c";
4091         Uri profileUri = TestUtil.maybeAddAccountQueryParameters(Profile.CONTENT_RAW_CONTACTS_URI,
4092                 account)
4093                 .buildUpon().appendQueryParameter(RawContacts.DATA_SET, dataSet).build();
4094         ContentValues values = new ContentValues();
4095         long rawContactId = ContentUris.parseId(mResolver.insert(profileUri, values));
4096         values.put(RawContacts._ID, rawContactId);
4097 
4098         // Check that querying for the profile gets the created raw contact.
4099         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, values);
4100     }
4101 
testLoadProfilePhoto()4102     public void testLoadProfilePhoto() throws IOException {
4103         long rawContactId = createBasicProfileContact(new ContentValues());
4104         insertPhoto(rawContactId, R.drawable.earth_normal);
4105         EvenMoreAsserts.assertImageRawData(getContext(),
4106                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.THUMBNAIL),
4107                 Contacts.openContactPhotoInputStream(mResolver, Profile.CONTENT_URI, false));
4108     }
4109 
testLoadProfileDisplayPhoto()4110     public void testLoadProfileDisplayPhoto() throws IOException {
4111         long rawContactId = createBasicProfileContact(new ContentValues());
4112         insertPhoto(rawContactId, R.drawable.earth_normal);
4113         EvenMoreAsserts.assertImageRawData(getContext(),
4114                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
4115                 Contacts.openContactPhotoInputStream(mResolver, Profile.CONTENT_URI, true));
4116     }
4117 
testPhonesWithStatusUpdate()4118     public void testPhonesWithStatusUpdate() {
4119 
4120         ContentValues values = new ContentValues();
4121         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
4122         long rawContactId = ContentUris.parseId(rawContactUri);
4123         DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe");
4124         Uri photoUri = insertPhoto(rawContactId);
4125         long photoId = ContentUris.parseId(photoUri);
4126         insertPhoneNumber(rawContactId, "18004664411");
4127         insertPhoneNumber(rawContactId, "18004664412");
4128         insertEmail(rawContactId, "goog411@acme.com");
4129         insertEmail(rawContactId, "goog412@acme.com");
4130 
4131         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411@acme.com",
4132                 StatusUpdates.INVISIBLE, "Bad",
4133                 StatusUpdates.CAPABILITY_HAS_CAMERA);
4134         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog412@acme.com",
4135                 StatusUpdates.AVAILABLE, "Good",
4136                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VOICE);
4137         long contactId = queryContactId(rawContactId);
4138 
4139         Uri uri = Data.CONTENT_URI;
4140 
4141         Cursor c = mResolver.query(uri, null, RawContacts.CONTACT_ID + "=" + contactId + " AND "
4142                 + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'", null, Phone.NUMBER);
4143         assertEquals(2, c.getCount());
4144 
4145         c.moveToFirst();
4146 
4147         values.clear();
4148         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
4149         values.put(Contacts.CONTACT_STATUS, "Bad");
4150         values.put(Contacts.DISPLAY_NAME, "John Doe");
4151         values.put(Phone.NUMBER, "18004664411");
4152         values.putNull(Phone.LABEL);
4153         values.put(RawContacts.CONTACT_ID, contactId);
4154         assertCursorValues(c, values);
4155 
4156         c.moveToNext();
4157 
4158         values.clear();
4159         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
4160         values.put(Contacts.CONTACT_STATUS, "Bad");
4161         values.put(Contacts.DISPLAY_NAME, "John Doe");
4162         values.put(Phone.NUMBER, "18004664412");
4163         values.putNull(Phone.LABEL);
4164         values.put(RawContacts.CONTACT_ID, contactId);
4165         assertCursorValues(c, values);
4166 
4167         c.close();
4168     }
4169 
testGroupQuery()4170     public void testGroupQuery() {
4171         Account account1 = new Account("a", "b");
4172         Account account2 = new Account("c", "d");
4173         long groupId1 = createGroup(account1, "e", "f");
4174         long groupId2 = createGroup(account2, "g", "h");
4175         Uri uri1 = TestUtil.maybeAddAccountQueryParameters(Groups.CONTENT_URI, account1);
4176         Uri uri2 = TestUtil.maybeAddAccountQueryParameters(Groups.CONTENT_URI, account2);
4177         assertEquals(1, getCount(uri1, null, null));
4178         assertEquals(1, getCount(uri2, null, null));
4179         assertStoredValue(uri1, Groups._ID + "=" + groupId1, null, Groups._ID, groupId1) ;
4180         assertStoredValue(uri2, Groups._ID + "=" + groupId2, null, Groups._ID, groupId2) ;
4181     }
4182 
testGroupInsert()4183     public void testGroupInsert() {
4184         ContentValues values = new ContentValues();
4185 
4186         values.put(Groups.ACCOUNT_NAME, "a");
4187         values.put(Groups.ACCOUNT_TYPE, "b");
4188         values.put(Groups.DATA_SET, "ds");
4189         values.put(Groups.SOURCE_ID, "c");
4190         values.put(Groups.VERSION, 42);
4191         values.put(Groups.GROUP_VISIBLE, 1);
4192         values.put(Groups.TITLE, "d");
4193         values.put(Groups.TITLE_RES, 1234);
4194         values.put(Groups.NOTES, "e");
4195         values.put(Groups.RES_PACKAGE, "f");
4196         values.put(Groups.SYSTEM_ID, "g");
4197         values.put(Groups.DELETED, 1);
4198         values.put(Groups.SYNC1, "h");
4199         values.put(Groups.SYNC2, "i");
4200         values.put(Groups.SYNC3, "j");
4201         values.put(Groups.SYNC4, "k");
4202 
4203         Uri rowUri = mResolver.insert(Groups.CONTENT_URI, values);
4204 
4205         values.put(Groups.DIRTY, 1);
4206         assertStoredValues(rowUri, values);
4207     }
4208 
testGroupCreationAfterMembershipInsert()4209     public void testGroupCreationAfterMembershipInsert() {
4210         long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount);
4211         Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1");
4212 
4213         long groupId = assertSingleGroup(NO_LONG, mAccount, "gsid1", null);
4214         assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
4215                 rawContactId1, groupId, "gsid1");
4216     }
4217 
testGroupReuseAfterMembershipInsert()4218     public void testGroupReuseAfterMembershipInsert() {
4219         long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount);
4220         long groupId1 = createGroup(mAccount, "gsid1", "title1");
4221         Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1");
4222 
4223         assertSingleGroup(groupId1, mAccount, "gsid1", "title1");
4224         assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
4225                 rawContactId1, groupId1, "gsid1");
4226     }
4227 
testGroupInsertFailureOnGroupIdConflict()4228     public void testGroupInsertFailureOnGroupIdConflict() {
4229         long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount);
4230         long groupId1 = createGroup(mAccount, "gsid1", "title1");
4231 
4232         ContentValues values = new ContentValues();
4233         values.put(GroupMembership.RAW_CONTACT_ID, rawContactId1);
4234         values.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
4235         values.put(GroupMembership.GROUP_SOURCE_ID, "gsid1");
4236         values.put(GroupMembership.GROUP_ROW_ID, groupId1);
4237         try {
4238             mResolver.insert(Data.CONTENT_URI, values);
4239             fail("the insert was expected to fail, but it succeeded");
4240         } catch (IllegalArgumentException e) {
4241             // this was expected
4242         }
4243     }
4244 
testGroupDelete_byAccountSelection()4245     public void testGroupDelete_byAccountSelection() {
4246         final Account account1 = new Account("accountName1", "accountType1");
4247         final Account account2 = new Account("accountName2", "accountType2");
4248 
4249         final long groupId1 = createGroup(account1, "sourceId1", "title1");
4250         final long groupId2 = createGroup(account2, "sourceId2", "title2");
4251         final long groupId3 = createGroup(account2, "sourceId3", "title3");
4252 
4253         final int numDeleted = mResolver.delete(Groups.CONTENT_URI,
4254                 Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?",
4255                 new String[]{account2.name, account2.type});
4256         assertEquals(2, numDeleted);
4257 
4258         ContentValues v1 = new ContentValues();
4259         v1.put(Groups._ID, groupId1);
4260         v1.put(Groups.DELETED, 0);
4261 
4262         ContentValues v2 = new ContentValues();
4263         v2.put(Groups._ID, groupId2);
4264         v2.put(Groups.DELETED, 1);
4265 
4266         ContentValues v3 = new ContentValues();
4267         v3.put(Groups._ID, groupId3);
4268         v3.put(Groups.DELETED, 1);
4269 
4270         assertStoredValues(Groups.CONTENT_URI, new ContentValues[] { v1, v2, v3 });
4271     }
4272 
testGroupDelete_byAccountParam()4273     public void testGroupDelete_byAccountParam() {
4274         final Account account1 = new Account("accountName1", "accountType1");
4275         final Account account2 = new Account("accountName2", "accountType2");
4276 
4277         final long groupId1 = createGroup(account1, "sourceId1", "title1");
4278         final long groupId2 = createGroup(account2, "sourceId2", "title2");
4279         final long groupId3 = createGroup(account2, "sourceId3", "title3");
4280 
4281         final int numDeleted = mResolver.delete(
4282                 Groups.CONTENT_URI.buildUpon()
4283                     .appendQueryParameter(Groups.ACCOUNT_NAME, account2.name)
4284                     .appendQueryParameter(Groups.ACCOUNT_TYPE, account2.type)
4285                     .build(),
4286                 null, null);
4287         assertEquals(2, numDeleted);
4288 
4289         ContentValues v1 = new ContentValues();
4290         v1.put(Groups._ID, groupId1);
4291         v1.put(Groups.DELETED, 0);
4292 
4293         ContentValues v2 = new ContentValues();
4294         v2.put(Groups._ID, groupId2);
4295         v2.put(Groups.DELETED, 1);
4296 
4297         ContentValues v3 = new ContentValues();
4298         v3.put(Groups._ID, groupId3);
4299         v3.put(Groups.DELETED, 1);
4300 
4301         assertStoredValues(Groups.CONTENT_URI, new ContentValues[] { v1, v2, v3 });
4302     }
4303 
testGroupSummaryQuery()4304     public void testGroupSummaryQuery() {
4305         final Account account1 = new Account("accountName1", "accountType1");
4306         final Account account2 = new Account("accountName2", "accountType2");
4307         final long groupId1 = createGroup(account1, "sourceId1", "title1");
4308         final long groupId2 = createGroup(account2, "sourceId2", "title2");
4309         final long groupId3 = createGroup(account2, "sourceId3", "title3");
4310 
4311         // Prepare raw contact id not used at all, to test group summary uri won't be confused
4312         // with it.
4313         final long rawContactId0 = RawContactUtil.createRawContactWithName(mResolver, "firstName0",
4314                 "lastName0");
4315 
4316         final long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "firstName1",
4317                 "lastName1");
4318         insertEmail(rawContactId1, "address1@email.com");
4319         insertGroupMembership(rawContactId1, groupId1);
4320 
4321         final long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "firstName2",
4322                 "lastName2");
4323         insertEmail(rawContactId2, "address2@email.com");
4324         insertPhoneNumber(rawContactId2, "222-222-2222");
4325         insertGroupMembership(rawContactId2, groupId1);
4326 
4327         ContentValues v1 = new ContentValues();
4328         v1.put(Groups._ID, groupId1);
4329         v1.put(Groups.TITLE, "title1");
4330         v1.put(Groups.SOURCE_ID, "sourceId1");
4331         v1.put(Groups.ACCOUNT_NAME, account1.name);
4332         v1.put(Groups.ACCOUNT_TYPE, account1.type);
4333         v1.put(Groups.SUMMARY_COUNT, 2);
4334         v1.put(Groups.SUMMARY_WITH_PHONES, 1);
4335 
4336         ContentValues v2 = new ContentValues();
4337         v2.put(Groups._ID, groupId2);
4338         v2.put(Groups.TITLE, "title2");
4339         v2.put(Groups.SOURCE_ID, "sourceId2");
4340         v2.put(Groups.ACCOUNT_NAME, account2.name);
4341         v2.put(Groups.ACCOUNT_TYPE, account2.type);
4342         v2.put(Groups.SUMMARY_COUNT, 0);
4343         v2.put(Groups.SUMMARY_WITH_PHONES, 0);
4344 
4345         ContentValues v3 = new ContentValues();
4346         v3.put(Groups._ID, groupId3);
4347         v3.put(Groups.TITLE, "title3");
4348         v3.put(Groups.SOURCE_ID, "sourceId3");
4349         v3.put(Groups.ACCOUNT_NAME, account2.name);
4350         v3.put(Groups.ACCOUNT_TYPE, account2.type);
4351         v3.put(Groups.SUMMARY_COUNT, 0);
4352         v3.put(Groups.SUMMARY_WITH_PHONES, 0);
4353 
4354         assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 });
4355 
4356         // Now rawContactId1 has two phone numbers.
4357         insertPhoneNumber(rawContactId1, "111-111-1111");
4358         insertPhoneNumber(rawContactId1, "111-111-1112");
4359         // Result should reflect it correctly (don't count phone numbers but raw contacts)
4360         v1.put(Groups.SUMMARY_WITH_PHONES, v1.getAsInteger(Groups.SUMMARY_WITH_PHONES) + 1);
4361         assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 });
4362 
4363         // Introduce new raw contact, pretending the user added another info.
4364         final long rawContactId3 = RawContactUtil.createRawContactWithName(mResolver, "firstName3",
4365                 "lastName3");
4366         insertEmail(rawContactId3, "address3@email.com");
4367         insertPhoneNumber(rawContactId3, "333-333-3333");
4368         insertGroupMembership(rawContactId3, groupId2);
4369         v2.put(Groups.SUMMARY_COUNT, v2.getAsInteger(Groups.SUMMARY_COUNT) + 1);
4370         v2.put(Groups.SUMMARY_WITH_PHONES, v2.getAsInteger(Groups.SUMMARY_WITH_PHONES) + 1);
4371 
4372         assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 });
4373 
4374         final Uri uri = Groups.CONTENT_SUMMARY_URI;
4375 
4376         // TODO Once SUMMARY_GROUP_COUNT_PER_ACCOUNT is supported remove all the if(false).
4377         if (false) {
4378             v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 1);
4379             v2.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2);
4380             v3.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2);
4381         } else {
4382             v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0);
4383             v2.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0);
4384             v3.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0);
4385         }
4386         assertStoredValues(uri, new ContentValues[] { v1, v2, v3 });
4387 
4388         // Introduce another group in account1, testing SUMMARY_GROUP_COUNT_PER_ACCOUNT correctly
4389         // reflects the change.
4390         final long groupId4 = createGroup(account1, "sourceId4", "title4");
4391         if (false) {
4392             v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
4393                     v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT) + 1);
4394         } else {
4395             v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0);
4396         }
4397         ContentValues v4 = new ContentValues();
4398         v4.put(Groups._ID, groupId4);
4399         v4.put(Groups.TITLE, "title4");
4400         v4.put(Groups.SOURCE_ID, "sourceId4");
4401         v4.put(Groups.ACCOUNT_NAME, account1.name);
4402         v4.put(Groups.ACCOUNT_TYPE, account1.type);
4403         v4.put(Groups.SUMMARY_COUNT, 0);
4404         v4.put(Groups.SUMMARY_WITH_PHONES, 0);
4405         if (false) {
4406             v4.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
4407                     v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT));
4408         } else {
4409             v4.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 0);
4410         }
4411         assertStoredValues(uri, new ContentValues[] { v1, v2, v3, v4 });
4412 
4413         // We change the tables dynamically according to the requested projection.
4414         // Make sure the SUMMARY_COUNT column exists
4415         v1.clear();
4416         v1.put(Groups.SUMMARY_COUNT, 2);
4417         v2.clear();
4418         v2.put(Groups.SUMMARY_COUNT, 1);
4419         v3.clear();
4420         v3.put(Groups.SUMMARY_COUNT, 0);
4421         v4.clear();
4422         v4.put(Groups.SUMMARY_COUNT, 0);
4423         assertStoredValuesWithProjection(uri, new ContentValues[] { v1, v2, v3, v4 });
4424     }
4425 
testSettingsQuery()4426     public void testSettingsQuery() {
4427         Account account1 = new Account("a", "b");
4428         Account account2 = new Account("c", "d");
4429         AccountWithDataSet account3 = new AccountWithDataSet("e", "f", "plus");
4430         createSettings(account1, "0", "0");
4431         createSettings(account2, "1", "1");
4432         createSettings(account3, "1", "0");
4433         Uri uri1 = TestUtil.maybeAddAccountQueryParameters(Settings.CONTENT_URI, account1);
4434         Uri uri2 = TestUtil.maybeAddAccountQueryParameters(Settings.CONTENT_URI, account2);
4435         Uri uri3 = Settings.CONTENT_URI.buildUpon()
4436                 .appendQueryParameter(RawContacts.ACCOUNT_NAME, account3.getAccountName())
4437                 .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account3.getAccountType())
4438                 .appendQueryParameter(RawContacts.DATA_SET, account3.getDataSet())
4439                 .build();
4440         assertEquals(1, getCount(uri1, null, null));
4441         assertEquals(1, getCount(uri2, null, null));
4442         assertEquals(1, getCount(uri3, null, null));
4443         assertStoredValue(uri1, Settings.SHOULD_SYNC, "0") ;
4444         assertStoredValue(uri1, Settings.UNGROUPED_VISIBLE, "0");
4445         assertStoredValue(uri2, Settings.SHOULD_SYNC, "1") ;
4446         assertStoredValue(uri2, Settings.UNGROUPED_VISIBLE, "1");
4447         assertStoredValue(uri3, Settings.SHOULD_SYNC, "1");
4448         assertStoredValue(uri3, Settings.UNGROUPED_VISIBLE, "0");
4449     }
4450 
testSettingsInsertionPreventsDuplicates()4451     public void testSettingsInsertionPreventsDuplicates() {
4452         Account account1 = new Account("a", "b");
4453         AccountWithDataSet account2 = new AccountWithDataSet("c", "d", "plus");
4454         createSettings(account1, "0", "0");
4455         createSettings(account2, "1", "1");
4456 
4457         // Now try creating the settings rows again.  It should update the existing settings rows.
4458         createSettings(account1, "1", "0");
4459         assertStoredValue(Settings.CONTENT_URI,
4460                 Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=?",
4461                 new String[] {"a", "b"}, Settings.SHOULD_SYNC, "1");
4462 
4463         createSettings(account2, "0", "1");
4464         assertStoredValue(Settings.CONTENT_URI,
4465                 Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=? AND " +
4466                 Settings.DATA_SET + "=?",
4467                 new String[] {"c", "d", "plus"}, Settings.SHOULD_SYNC, "0");
4468     }
4469 
testSettingsDeletion()4470     public void testSettingsDeletion() {
4471         Account account = new Account("a", "b");
4472         Uri settingUri = createSettings(account, "0", "1");
4473         long rawContactId = RawContactUtil.createRawContact(mResolver, account);
4474 
4475         int count = mResolver.delete(settingUri, null, null);
4476 
4477         // Settings cannot be deleted when there are still raw contacts for the account.
4478         assertEquals(0, count);
4479 
4480         assertStoredValue(Settings.CONTENT_URI,
4481                 Settings.ACCOUNT_NAME + "= ? AND " + Settings.ACCOUNT_TYPE + "= ?",
4482                 new String[] {"a", "b"}, Settings.UNGROUPED_VISIBLE, "1");
4483 
4484         RawContactUtil.delete(mResolver, rawContactId, true);
4485 
4486         count = mResolver.delete(settingUri, null, null);
4487 
4488         assertEquals(1, count);
4489         assertRowCount(0, Settings.CONTENT_URI, null, null);
4490     }
4491 
testSettingsUpdate()4492     public void testSettingsUpdate() {
4493         Account account1 = new Account("a", "b");
4494         Account account2 = new Account("c", "d");
4495         Account account3 = new Account("e", "f");
4496         createSettings(account1, "0", "0");
4497         createSettings(account2, "0", "0");
4498         createSettings(account3, "0", "0");
4499 
4500         ContentValues values = new ContentValues();
4501         values.put(Settings.UNGROUPED_VISIBLE, 1);
4502         int count = mResolver.update(Settings.CONTENT_URI, values, null, null);
4503 
4504         assertEquals(3, count);
4505         assertStoredValues(Settings.CONTENT_URI,
4506                 cv(Settings.UNGROUPED_VISIBLE, 1),
4507                 cv(Settings.UNGROUPED_VISIBLE, 1),
4508                 cv(Settings.UNGROUPED_VISIBLE, 1));
4509 
4510         values.put(Settings.SHOULD_SYNC, 1);
4511         count = mResolver.update(Settings.CONTENT_URI, values,
4512                 Settings.ACCOUNT_NAME  + "=?", new String[] {"a"});
4513 
4514         assertEquals(1, count);
4515         assertStoredValues(Settings.CONTENT_URI,
4516                 cv(Settings.ACCOUNT_NAME, "a",
4517                         Settings.SHOULD_SYNC, 1),
4518                 cv(Settings.ACCOUNT_NAME, "c",
4519                         Settings.SHOULD_SYNC, 0),
4520                 cv(Settings.ACCOUNT_NAME, "e",
4521                         Settings.SHOULD_SYNC, 0));
4522 
4523         values.clear();
4524         // Settings are stored in the accounts table but updates shouldn't be allowed to modify
4525         // the other non-Settings columns.
4526         values.put(Settings.ACCOUNT_NAME, "x");
4527         values.put(Settings.ACCOUNT_TYPE, "y");
4528         values.put(Settings.DATA_SET, "z");
4529         mResolver.update(Settings.CONTENT_URI, values, null, null);
4530 
4531         values.put(AccountsColumns.SIM_EF_TYPE, 1);
4532         values.put(AccountsColumns.SIM_SLOT_INDEX, 1);
4533         try {
4534             mResolver.update(Settings.CONTENT_URI, values, null, null);
4535         } catch (Exception e) {
4536             // ignored. We just care that the update didn't change the data
4537         }
4538 
4539         assertStoredValuesDb("SELECT * FROM " + Tables.ACCOUNTS, null,
4540                 cv(
4541                         Settings.ACCOUNT_NAME, "a",
4542                         Settings.ACCOUNT_TYPE, "b",
4543                         Settings.DATA_SET, null,
4544                         AccountsColumns.SIM_SLOT_INDEX, null,
4545                         AccountsColumns.SIM_EF_TYPE, null
4546                 ),
4547                 cv(
4548                         Settings.ACCOUNT_NAME, "c",
4549                         Settings.ACCOUNT_TYPE, "d",
4550                         Settings.DATA_SET, null,
4551                         AccountsColumns.SIM_SLOT_INDEX, null,
4552                         AccountsColumns.SIM_EF_TYPE, null
4553                 ),
4554                 cv(
4555                         Settings.ACCOUNT_NAME, "e",
4556                         Settings.ACCOUNT_TYPE, "f",
4557                         Settings.DATA_SET, null,
4558                         AccountsColumns.SIM_SLOT_INDEX, null,
4559                         AccountsColumns.SIM_EF_TYPE, null
4560                 ));
4561     }
4562 
testSettingsLocalAccount()4563     public void testSettingsLocalAccount() {
4564         AccountWithDataSet localAccount = AccountWithDataSet.LOCAL;
4565 
4566         // It's not possible to insert the local account directly into settings but it will be
4567         // created automatically when a raw contact is created for it.
4568         RawContactUtil.createRawContactWithAccountDataSet(
4569                 mResolver, localAccount.getAccountName(),
4570                 localAccount.getAccountType(), localAccount.getDataSet());
4571 
4572         ContentValues values = new ContentValues();
4573         values.put(Settings.ACCOUNT_NAME, localAccount.getAccountName());
4574         values.put(Settings.ACCOUNT_TYPE, localAccount.getAccountType());
4575         values.put(Settings.DATA_SET, localAccount.getDataSet());
4576         ContentValues expectedValues = new ContentValues(values);
4577         // The defaults for the local account are opposite of other accounts.
4578         expectedValues.put(Settings.UNGROUPED_VISIBLE, "1");
4579         expectedValues.put(Settings.SHOULD_SYNC, "0");
4580 
4581         assertStoredValues(Settings.CONTENT_URI, expectedValues);
4582 
4583         values.put(Settings.SHOULD_SYNC, 1);
4584         values.put(Settings.UNGROUPED_VISIBLE, 0);
4585         mResolver.update(Settings.CONTENT_URI, values, null, null);
4586 
4587         expectedValues.put(Settings.UNGROUPED_VISIBLE, "0");
4588         expectedValues.put(Settings.SHOULD_SYNC, "1");
4589         assertStoredValues(Settings.CONTENT_URI, expectedValues);
4590 
4591         // Empty strings should also be the local account.
4592         values.put(Settings.ACCOUNT_NAME, "");
4593         values.put(Settings.ACCOUNT_TYPE, "");
4594         values.put(Settings.DATA_SET, "");
4595         mResolver.insert(Settings.CONTENT_URI, values);
4596 
4597         assertRowCount(1, Settings.CONTENT_URI, null, null);
4598     }
4599 
testDisplayNameParsingWhenPartsUnspecified()4600     public void testDisplayNameParsingWhenPartsUnspecified() {
4601         long rawContactId = RawContactUtil.createRawContact(mResolver);
4602         ContentValues values = new ContentValues();
4603         values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
4604         DataUtil.insertStructuredName(mResolver, rawContactId, values);
4605 
4606         assertStructuredName(rawContactId, "Mr.", "John", "Kevin", "von Smith", "Jr.");
4607     }
4608 
testDisplayNameParsingWhenPartsAreNull()4609     public void testDisplayNameParsingWhenPartsAreNull() {
4610         long rawContactId = RawContactUtil.createRawContact(mResolver);
4611         ContentValues values = new ContentValues();
4612         values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
4613         values.putNull(StructuredName.GIVEN_NAME);
4614         values.putNull(StructuredName.FAMILY_NAME);
4615         DataUtil.insertStructuredName(mResolver, rawContactId, values);
4616         assertStructuredName(rawContactId, "Mr.", "John", "Kevin", "von Smith", "Jr.");
4617     }
4618 
testDisplayNameParsingWhenPartsSpecified()4619     public void testDisplayNameParsingWhenPartsSpecified() {
4620         long rawContactId = RawContactUtil.createRawContact(mResolver);
4621         ContentValues values = new ContentValues();
4622         values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
4623         values.put(StructuredName.FAMILY_NAME, "Johnson");
4624         DataUtil.insertStructuredName(mResolver, rawContactId, values);
4625 
4626         assertStructuredName(rawContactId, null, null, null, "Johnson", null);
4627     }
4628 
testContactWithoutPhoneticName()4629     public void testContactWithoutPhoneticName() {
4630         ContactLocaleUtils.setLocaleForTest(Locale.ENGLISH);
4631         final long rawContactId = RawContactUtil.createRawContact(mResolver, null);
4632 
4633         ContentValues values = new ContentValues();
4634         values.put(StructuredName.PREFIX, "Mr");
4635         values.put(StructuredName.GIVEN_NAME, "John");
4636         values.put(StructuredName.MIDDLE_NAME, "K.");
4637         values.put(StructuredName.FAMILY_NAME, "Doe");
4638         values.put(StructuredName.SUFFIX, "Jr.");
4639         Uri dataUri = DataUtil.insertStructuredName(mResolver, rawContactId, values);
4640 
4641         values.clear();
4642         values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
4643         values.put(RawContacts.DISPLAY_NAME_PRIMARY, "Mr John K. Doe, Jr.");
4644         values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "Mr Doe, John K., Jr.");
4645         values.putNull(RawContacts.PHONETIC_NAME);
4646         values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
4647         values.put(RawContacts.SORT_KEY_PRIMARY, "John K. Doe, Jr.");
4648         values.put(RawContactsColumns.PHONEBOOK_LABEL_PRIMARY, "J");
4649         values.put(RawContacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr.");
4650         values.put(RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "D");
4651 
4652         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
4653         assertStoredValues(rawContactUri, values);
4654 
4655         values.clear();
4656         values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
4657         values.put(Contacts.DISPLAY_NAME_PRIMARY, "Mr John K. Doe, Jr.");
4658         values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "Mr Doe, John K., Jr.");
4659         values.putNull(Contacts.PHONETIC_NAME);
4660         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
4661         values.put(Contacts.SORT_KEY_PRIMARY, "John K. Doe, Jr.");
4662         values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "J");
4663         values.put(Contacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr.");
4664         values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "D");
4665 
4666         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
4667                 queryContactId(rawContactId));
4668         assertStoredValues(contactUri, values);
4669 
4670         // The same values should be available through a join with Data
4671         assertStoredValues(dataUri, values);
4672     }
4673 
testContactWithChineseName()4674     public void testContactWithChineseName() {
4675         if (!hasChineseCollator()) {
4676             return;
4677         }
4678         ContactLocaleUtils.setLocaleForTest(Locale.SIMPLIFIED_CHINESE);
4679 
4680         long rawContactId = RawContactUtil.createRawContact(mResolver, null);
4681 
4682         ContentValues values = new ContentValues();
4683         // "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B"
4684         values.put(StructuredName.DISPLAY_NAME, "\u6BB5\u5C0F\u6D9B");
4685         Uri dataUri = DataUtil.insertStructuredName(mResolver, rawContactId, values);
4686 
4687         values.clear();
4688         values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
4689         values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B");
4690         values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
4691         values.putNull(RawContacts.PHONETIC_NAME);
4692         values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
4693         values.put(RawContacts.SORT_KEY_PRIMARY, "\u6BB5\u5C0F\u6D9B");
4694         values.put(RawContactsColumns.PHONEBOOK_LABEL_PRIMARY, "D");
4695         values.put(RawContacts.SORT_KEY_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
4696         values.put(RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "D");
4697 
4698         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
4699         assertStoredValues(rawContactUri, values);
4700 
4701         values.clear();
4702         values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
4703         values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B");
4704         values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
4705         values.putNull(Contacts.PHONETIC_NAME);
4706         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
4707         values.put(Contacts.SORT_KEY_PRIMARY, "\u6BB5\u5C0F\u6D9B");
4708         values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "D");
4709         values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
4710         values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "D");
4711 
4712         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
4713                 queryContactId(rawContactId));
4714         assertStoredValues(contactUri, values);
4715 
4716         // The same values should be available through a join with Data
4717         assertStoredValues(dataUri, values);
4718     }
4719 
testJapaneseNameContactInEnglishLocale()4720     public void testJapaneseNameContactInEnglishLocale() {
4721         // Need Japanese locale data for transliteration
4722         if (!hasJapaneseCollator()) {
4723             return;
4724         }
4725         ContactLocaleUtils.setLocaleForTest(Locale.US);
4726         long rawContactId = RawContactUtil.createRawContact(mResolver, null);
4727 
4728         ContentValues values = new ContentValues();
4729         values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77");
4730         values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046");
4731         DataUtil.insertStructuredName(mResolver, rawContactId, values);
4732 
4733         long contactId = queryContactId(rawContactId);
4734         // en_US should behave same as ja_JP (match on Hiragana and Romaji
4735         // but not Pinyin)
4736         assertContactFilter(contactId, "\u304B\u3044\u304F\u3046");
4737         assertContactFilter(contactId, "kaiku");
4738         assertContactFilterNoResult("kong");
4739     }
4740 
testContactWithJapaneseName()4741     public void testContactWithJapaneseName() {
4742         if (!hasJapaneseCollator()) {
4743             return;
4744         }
4745         ContactLocaleUtils.setLocaleForTest(Locale.JAPAN);
4746         long rawContactId = RawContactUtil.createRawContact(mResolver, null);
4747 
4748         ContentValues values = new ContentValues();
4749         values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77");
4750         values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046");
4751         Uri dataUri = DataUtil.insertStructuredName(mResolver, rawContactId, values);
4752 
4753         values.clear();
4754         values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
4755         values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77");
4756         values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77");
4757         values.put(RawContacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046");
4758         values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
4759         values.put(RawContacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046");
4760         values.put(RawContacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046");
4761         values.put(RawContactsColumns.PHONEBOOK_LABEL_PRIMARY, "\u304B");
4762         values.put(RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "\u304B");
4763 
4764         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
4765         assertStoredValues(rawContactUri, values);
4766 
4767         values.clear();
4768         values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
4769         values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77");
4770         values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77");
4771         values.put(Contacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046");
4772         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
4773         values.put(Contacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046");
4774         values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046");
4775         values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "\u304B");
4776         values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "\u304B");
4777 
4778         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
4779                 queryContactId(rawContactId));
4780         assertStoredValues(contactUri, values);
4781 
4782         // The same values should be available through a join with Data
4783         assertStoredValues(dataUri, values);
4784 
4785         long contactId = queryContactId(rawContactId);
4786         // ja_JP should match on Hiragana and Romaji but not Pinyin
4787         assertContactFilter(contactId, "\u304B\u3044\u304F\u3046");
4788         assertContactFilter(contactId, "kaiku");
4789         assertContactFilterNoResult("kong");
4790     }
4791 
testDisplayNameUpdate()4792     public void testDisplayNameUpdate() {
4793         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
4794         insertEmail(rawContactId1, "potato@acme.com", true);
4795 
4796         long rawContactId2 = RawContactUtil.createRawContact(mResolver);
4797         insertPhoneNumber(rawContactId2, "123456789", true);
4798 
4799         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
4800                 rawContactId1, rawContactId2);
4801 
4802         assertAggregated(rawContactId1, rawContactId2, "123456789");
4803 
4804         DataUtil.insertStructuredName(mResolver, rawContactId2, "Potato", "Head");
4805 
4806         assertAggregated(rawContactId1, rawContactId2, "Potato Head");
4807         assertNetworkNotified(true);
4808     }
4809 
testDisplayNameFromData()4810     public void testDisplayNameFromData() {
4811         long rawContactId = RawContactUtil.createRawContact(mResolver);
4812         long contactId = queryContactId(rawContactId);
4813         ContentValues values = new ContentValues();
4814 
4815         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4816 
4817         assertStoredValue(uri, Contacts.DISPLAY_NAME, null);
4818         insertEmail(rawContactId, "mike@monstersinc.com");
4819         assertStoredValue(uri, Contacts.DISPLAY_NAME, "mike@monstersinc.com");
4820 
4821         insertEmail(rawContactId, "james@monstersinc.com", true);
4822         assertStoredValue(uri, Contacts.DISPLAY_NAME, "james@monstersinc.com");
4823 
4824         insertPhoneNumber(rawContactId, "1-800-466-4411");
4825         assertStoredValue(uri, Contacts.DISPLAY_NAME, "1-800-466-4411");
4826 
4827         // If there are title and company, the company is display name.
4828         values.clear();
4829         values.put(Organization.COMPANY, "Monsters Inc");
4830         Uri organizationUri = insertOrganization(rawContactId, values);
4831         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Monsters Inc");
4832 
4833         // If there is nickname, that is display name.
4834         insertNickname(rawContactId, "Sully");
4835         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Sully");
4836 
4837         // If there is structured name, that is display name.
4838         values.clear();
4839         values.put(StructuredName.GIVEN_NAME, "James");
4840         values.put(StructuredName.MIDDLE_NAME, "P.");
4841         values.put(StructuredName.FAMILY_NAME, "Sullivan");
4842         DataUtil.insertStructuredName(mResolver, rawContactId, values);
4843         assertStoredValue(uri, Contacts.DISPLAY_NAME, "James P. Sullivan");
4844     }
4845 
testDisplayNameFromOrganizationWithoutPhoneticName()4846     public void testDisplayNameFromOrganizationWithoutPhoneticName() {
4847         long rawContactId = RawContactUtil.createRawContact(mResolver);
4848         long contactId = queryContactId(rawContactId);
4849         ContentValues values = new ContentValues();
4850 
4851         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4852 
4853         // If there is title without company, the title is display name.
4854         values.clear();
4855         values.put(Organization.TITLE, "Protagonist");
4856         Uri organizationUri = insertOrganization(rawContactId, values);
4857         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Protagonist");
4858 
4859         // If there are title and company, the company is display name.
4860         values.clear();
4861         values.put(Organization.COMPANY, "Monsters Inc");
4862         mResolver.update(organizationUri, values, null, null);
4863 
4864         values.clear();
4865         values.put(Contacts.DISPLAY_NAME, "Monsters Inc");
4866         values.putNull(Contacts.PHONETIC_NAME);
4867         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
4868         values.put(Contacts.SORT_KEY_PRIMARY, "Monsters Inc");
4869         values.put(Contacts.SORT_KEY_ALTERNATIVE, "Monsters Inc");
4870         values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "M");
4871         values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "M");
4872         assertStoredValues(uri, values);
4873     }
4874 
testDisplayNameFromOrganizationWithJapanesePhoneticName()4875     public void testDisplayNameFromOrganizationWithJapanesePhoneticName() {
4876         if (!hasJapaneseCollator()) {
4877             return;
4878         }
4879         ContactLocaleUtils.setLocaleForTest(Locale.JAPAN);
4880         long rawContactId = RawContactUtil.createRawContact(mResolver);
4881         long contactId = queryContactId(rawContactId);
4882         ContentValues values = new ContentValues();
4883 
4884         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4885 
4886         // If there is title without company, the title is display name.
4887         values.clear();
4888         values.put(Organization.COMPANY, "DoCoMo");
4889         values.put(Organization.PHONETIC_NAME, "\u30C9\u30B3\u30E2");
4890         Uri organizationUri = insertOrganization(rawContactId, values);
4891 
4892         values.clear();
4893         values.put(Contacts.DISPLAY_NAME, "DoCoMo");
4894         values.put(Contacts.PHONETIC_NAME, "\u30C9\u30B3\u30E2");
4895         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
4896         values.put(Contacts.SORT_KEY_PRIMARY, "\u30C9\u30B3\u30E2");
4897         values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u30C9\u30B3\u30E2");
4898         values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "\u305F");
4899         values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "\u305F");
4900         assertStoredValues(uri, values);
4901     }
4902 
testDisplayNameFromOrganizationWithChineseName()4903     public void testDisplayNameFromOrganizationWithChineseName() {
4904         if (!hasChineseCollator()) {
4905             return;
4906         }
4907         ContactLocaleUtils.setLocaleForTest(Locale.SIMPLIFIED_CHINESE);
4908 
4909         long rawContactId = RawContactUtil.createRawContact(mResolver);
4910         long contactId = queryContactId(rawContactId);
4911         ContentValues values = new ContentValues();
4912 
4913         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
4914 
4915         // If there is title without company, the title is display name.
4916         values.clear();
4917         values.put(Organization.COMPANY, "\u4E2D\u56FD\u7535\u4FE1");
4918         Uri organizationUri = insertOrganization(rawContactId, values);
4919 
4920         values.clear();
4921         values.put(Contacts.DISPLAY_NAME, "\u4E2D\u56FD\u7535\u4FE1");
4922         values.putNull(Contacts.PHONETIC_NAME);
4923         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
4924         values.put(Contacts.SORT_KEY_PRIMARY, "\u4E2D\u56FD\u7535\u4FE1");
4925         values.put(ContactsColumns.PHONEBOOK_LABEL_PRIMARY, "Z");
4926         values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u4E2D\u56FD\u7535\u4FE1");
4927         values.put(ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, "Z");
4928         assertStoredValues(uri, values);
4929     }
4930 
testLookupByOrganization()4931     public void testLookupByOrganization() {
4932         long rawContactId = RawContactUtil.createRawContact(mResolver);
4933         long contactId = queryContactId(rawContactId);
4934         ContentValues values = new ContentValues();
4935 
4936         values.clear();
4937         values.put(Organization.COMPANY, "acmecorp");
4938         values.put(Organization.TITLE, "president");
4939         Uri organizationUri = insertOrganization(rawContactId, values);
4940 
4941         assertContactFilter(contactId, "acmecorp");
4942         assertContactFilter(contactId, "president");
4943 
4944         values.clear();
4945         values.put(Organization.DEPARTMENT, "software");
4946         mResolver.update(organizationUri, values, null, null);
4947 
4948         assertContactFilter(contactId, "acmecorp");
4949         assertContactFilter(contactId, "president");
4950 
4951         values.clear();
4952         values.put(Organization.COMPANY, "incredibles");
4953         mResolver.update(organizationUri, values, null, null);
4954 
4955         assertContactFilter(contactId, "incredibles");
4956         assertContactFilter(contactId, "president");
4957 
4958         values.clear();
4959         values.put(Organization.TITLE, "director");
4960         mResolver.update(organizationUri, values, null, null);
4961 
4962         assertContactFilter(contactId, "incredibles");
4963         assertContactFilter(contactId, "director");
4964 
4965         values.clear();
4966         values.put(Organization.COMPANY, "monsters");
4967         values.put(Organization.TITLE, "scarer");
4968         mResolver.update(organizationUri, values, null, null);
4969 
4970         assertContactFilter(contactId, "monsters");
4971         assertContactFilter(contactId, "scarer");
4972     }
4973 
assertContactFilter(long contactId, String filter)4974     private void assertContactFilter(long contactId, String filter) {
4975         Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
4976         assertStoredValue(filterUri, Contacts._ID, contactId);
4977     }
4978 
assertContactFilterNoResult(String filter)4979     private void assertContactFilterNoResult(String filter) {
4980         Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
4981         assertEquals(0, getCount(filterUri, null, null));
4982     }
4983 
testSearchSnippetOrganization()4984     public void testSearchSnippetOrganization() throws Exception {
4985         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
4986         long contactId = queryContactId(rawContactId);
4987 
4988         // Some random data element
4989         insertEmail(rawContactId, "inc@corp.com");
4990 
4991         ContentValues values = new ContentValues();
4992         values.clear();
4993         values.put(Organization.COMPANY, "acmecorp");
4994         values.put(Organization.TITLE, "engineer");
4995         Uri organizationUri = insertOrganization(rawContactId, values);
4996 
4997         // Add another matching organization
4998         values.put(Organization.COMPANY, "acmeinc");
4999         insertOrganization(rawContactId, values);
5000 
5001         // Add another non-matching organization
5002         values.put(Organization.COMPANY, "corpacme");
5003         insertOrganization(rawContactId, values);
5004 
5005         // And another data element
5006         insertEmail(rawContactId, "emca@corp.com", true, Email.TYPE_CUSTOM, "Custom");
5007 
5008         Uri filterUri = buildFilterUri("acme", true);
5009 
5010         values.clear();
5011         values.put(Contacts._ID, contactId);
5012         values.put(SearchSnippets.SNIPPET, "acmecorp");
5013         assertContainsValues(filterUri, values);
5014     }
5015 
testSearchSnippetEmail()5016     public void testSearchSnippetEmail() throws Exception {
5017         long rawContactId = RawContactUtil.createRawContact(mResolver);
5018         long contactId = queryContactId(rawContactId);
5019         ContentValues values = new ContentValues();
5020 
5021         DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe");
5022         Uri dataUri = insertEmail(rawContactId, "acme@corp.com", true, Email.TYPE_CUSTOM, "Custom");
5023 
5024         Uri filterUri = buildFilterUri("acme", true);
5025 
5026         values.clear();
5027         values.put(Contacts._ID, contactId);
5028         values.put(SearchSnippets.SNIPPET, "acme@corp.com");
5029         assertStoredValues(filterUri, values);
5030     }
5031 
testCountPhoneNumberDigits()5032     public void testCountPhoneNumberDigits() {
5033         assertEquals(10, ContactsProvider2.countPhoneNumberDigits("86 (0) 5-55-12-34"));
5034         assertEquals(10, ContactsProvider2.countPhoneNumberDigits("860 555-1234"));
5035         assertEquals(3, ContactsProvider2.countPhoneNumberDigits("860"));
5036         assertEquals(10, ContactsProvider2.countPhoneNumberDigits("8605551234"));
5037         assertEquals(6, ContactsProvider2.countPhoneNumberDigits("860555"));
5038         assertEquals(6, ContactsProvider2.countPhoneNumberDigits("860 555"));
5039         assertEquals(6, ContactsProvider2.countPhoneNumberDigits("860-555"));
5040         assertEquals(12, ContactsProvider2.countPhoneNumberDigits("+441234098765"));
5041         assertEquals(0, ContactsProvider2.countPhoneNumberDigits("44+1234098765"));
5042         assertEquals(0, ContactsProvider2.countPhoneNumberDigits("+441234098foo"));
5043     }
5044 
testSearchSnippetPhone()5045     public void testSearchSnippetPhone() throws Exception {
5046         long rawContactId = RawContactUtil.createRawContact(mResolver);
5047         long contactId = queryContactId(rawContactId);
5048         ContentValues values = new ContentValues();
5049 
5050         DataUtil.insertStructuredName(mResolver, rawContactId, "Cave", "Johnson");
5051         insertPhoneNumber(rawContactId, "(860) 555-1234");
5052 
5053         values.clear();
5054         values.put(Contacts._ID, contactId);
5055         values.put(SearchSnippets.SNIPPET, "[(860) 555-1234]");
5056 
5057         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
5058                 Uri.encode("86 (0) 5-55-12-34")), values);
5059         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
5060                 Uri.encode("860 555-1234")), values);
5061         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
5062                 Uri.encode("860")), values);
5063         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
5064                 Uri.encode("8605551234")), values);
5065         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
5066                 Uri.encode("860555")), values);
5067         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
5068                 Uri.encode("860 555")), values);
5069         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
5070                 Uri.encode("860-555")), values);
5071     }
5072 
buildFilterUri(String query, boolean deferredSnippeting)5073     private Uri buildFilterUri(String query, boolean deferredSnippeting) {
5074         Uri.Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon()
5075                 .appendPath(Uri.encode(query));
5076         if (deferredSnippeting) {
5077             builder.appendQueryParameter(ContactsContract.DEFERRED_SNIPPETING, "1");
5078         }
5079         return builder.build();
5080     }
5081 
createSnippetContentValues(long contactId, String snippet)5082     public ContentValues createSnippetContentValues(long contactId, String snippet) {
5083         final ContentValues values = new ContentValues();
5084         values.clear();
5085         values.put(Contacts._ID, contactId);
5086         values.put(SearchSnippets.SNIPPET, snippet);
5087         return values;
5088     }
5089 
testSearchSnippetNickname()5090     public void testSearchSnippetNickname() throws Exception {
5091         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
5092         long contactId = queryContactId(rawContactId);
5093         ContentValues values = new ContentValues();
5094 
5095         Uri dataUri = insertNickname(rawContactId, "Incredible");
5096 
5097         Uri filterUri = buildFilterUri("inc", true);
5098 
5099         values.clear();
5100         values.put(Contacts._ID, contactId);
5101         values.put(SearchSnippets.SNIPPET, "Incredible");
5102         assertStoredValues(filterUri, values);
5103     }
5104 
testSearchSnippetEmptyForNameInDisplayName()5105     public void testSearchSnippetEmptyForNameInDisplayName() throws Exception {
5106         long rawContactId = RawContactUtil.createRawContact(mResolver);
5107         long contactId = queryContactId(rawContactId);
5108         DataUtil.insertStructuredName(mResolver, rawContactId, "Cave", "Johnson");
5109         insertEmail(rawContactId, "cave@aperturescience.com", true);
5110 
5111         ContentValues snippet = createSnippetContentValues(contactId, "cave@aperturescience.com");
5112 
5113         assertContainsValues(buildFilterUri("cave", true), snippet);
5114         assertContainsValues(buildFilterUri("john", true), snippet);
5115     }
5116 
testSearchSnippetEmptyForNicknameInDisplayName()5117     public void testSearchSnippetEmptyForNicknameInDisplayName() throws Exception {
5118         long rawContactId = RawContactUtil.createRawContact(mResolver);
5119         long contactId = queryContactId(rawContactId);
5120         insertNickname(rawContactId, "Caveman");
5121         insertEmail(rawContactId, "cave@aperturescience.com", true);
5122 
5123         ContentValues snippet = createSnippetContentValues(contactId, "cave@aperturescience.com");
5124 
5125         assertContainsValues(buildFilterUri("cave", true), snippet);
5126     }
5127 
testSearchSnippetEmptyForCompanyInDisplayName()5128     public void testSearchSnippetEmptyForCompanyInDisplayName() throws Exception {
5129         long rawContactId = RawContactUtil.createRawContact(mResolver);
5130         long contactId = queryContactId(rawContactId);
5131         ContentValues company = new ContentValues();
5132         company.clear();
5133         company.put(Organization.COMPANY, "Aperture Science");
5134         company.put(Organization.TITLE, "President");
5135         insertOrganization(rawContactId, company);
5136         insertEmail(rawContactId, "aperturepresident@aperturescience.com", true);
5137 
5138         ContentValues snippet = createSnippetContentValues(contactId, "aperturepresident");
5139 
5140         assertContainsValues(buildFilterUri("aperture", true), snippet);
5141     }
5142 
testSearchSnippetEmptyForPhoneInDisplayName()5143     public void testSearchSnippetEmptyForPhoneInDisplayName() throws Exception {
5144         long rawContactId = RawContactUtil.createRawContact(mResolver);
5145         long contactId = queryContactId(rawContactId);
5146         insertPhoneNumber(rawContactId, "860-555-1234");
5147         insertEmail(rawContactId, "860@aperturescience.com", true);
5148 
5149         ContentValues snippet = createSnippetContentValues(contactId, "860-555-1234");
5150 
5151         assertContainsValues(buildFilterUri("860", true), snippet);
5152     }
5153 
testSearchSnippetEmptyForEmailInDisplayName()5154     public void testSearchSnippetEmptyForEmailInDisplayName() throws Exception {
5155         long rawContactId = RawContactUtil.createRawContact(mResolver);
5156         long contactId = queryContactId(rawContactId);
5157         insertEmail(rawContactId, "cave@aperturescience.com", true);
5158         insertNote(rawContactId, "Cave Johnson is president of Aperture Science");
5159 
5160         ContentValues snippet = createSnippetContentValues(contactId,
5161                 "Cave Johnson is president of Aperture Science");
5162 
5163         assertContainsValues(buildFilterUri("cave", true), snippet);
5164     }
5165 
testDisplayNameUpdateFromStructuredNameUpdate()5166     public void testDisplayNameUpdateFromStructuredNameUpdate() {
5167         long rawContactId = RawContactUtil.createRawContact(mResolver);
5168         Uri nameUri = DataUtil.insertStructuredName(mResolver, rawContactId, "Slinky", "Dog");
5169 
5170         long contactId = queryContactId(rawContactId);
5171 
5172         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
5173         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky Dog");
5174 
5175         ContentValues values = new ContentValues();
5176         values.putNull(StructuredName.FAMILY_NAME);
5177 
5178         mResolver.update(nameUri, values, null, null);
5179         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky");
5180 
5181         values.putNull(StructuredName.GIVEN_NAME);
5182 
5183         mResolver.update(nameUri, values, null, null);
5184         assertStoredValue(uri, Contacts.DISPLAY_NAME, null);
5185 
5186         values.put(StructuredName.FAMILY_NAME, "Dog");
5187         mResolver.update(nameUri, values, null, null);
5188 
5189         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Dog");
5190     }
5191 
testInsertDataWithContentProviderOperations()5192     public void testInsertDataWithContentProviderOperations() throws Exception {
5193         ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
5194                 .withValues(new ContentValues())
5195                 .build();
5196         ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI)
5197                 .withValueBackReference(Data.RAW_CONTACT_ID, 0)
5198                 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
5199                 .withValue(StructuredName.GIVEN_NAME, "John")
5200                 .withValue(StructuredName.FAMILY_NAME, "Doe")
5201                 .build();
5202         ContentProviderResult[] results =
5203                 mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(cpo1, cpo2));
5204         long contactId = queryContactId(ContentUris.parseId(results[0].uri));
5205         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
5206         assertStoredValue(uri, Contacts.DISPLAY_NAME, "John Doe");
5207     }
5208 
testSendToVoicemailDefault()5209     public void testSendToVoicemailDefault() {
5210         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
5211         long contactId = queryContactId(rawContactId);
5212 
5213         Cursor c = queryContact(contactId);
5214         assertTrue(c.moveToNext());
5215         int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL));
5216         assertEquals(0, sendToVoicemail);
5217         c.close();
5218     }
5219 
testSetSendToVoicemailAndRingtone()5220     public void testSetSendToVoicemailAndRingtone() {
5221         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
5222         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
5223         assertDirty(rawContactUri, true);
5224         clearDirty(rawContactUri);
5225         long contactId = queryContactId(rawContactId);
5226 
5227         updateSendToVoicemailAndRingtone(contactId, true, "foo");
5228         assertSendToVoicemailAndRingtone(contactId, true, "foo");
5229         assertNetworkNotified(false);
5230         assertDirty(rawContactUri, false);
5231         assertMetadataDirty(rawContactUri, false);
5232 
5233         updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar");
5234         assertSendToVoicemailAndRingtone(contactId, false, "bar");
5235         assertNetworkNotified(false);
5236         assertDirty(rawContactUri, false);
5237         assertMetadataDirty(rawContactUri, false);
5238     }
5239 
testSendToVoicemailAndRingtoneAfterAggregation()5240     public void testSendToVoicemailAndRingtoneAfterAggregation() {
5241         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "a", "b");
5242         long contactId1 = queryContactId(rawContactId1);
5243         updateSendToVoicemailAndRingtone(contactId1, true, "foo");
5244 
5245         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "c", "d");
5246         long contactId2 = queryContactId(rawContactId2);
5247         updateSendToVoicemailAndRingtone(contactId2, true, "bar");
5248 
5249         // Aggregate them
5250         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
5251                 rawContactId1, rawContactId2);
5252 
5253         // Both contacts had "send to VM", the contact now has the same value
5254         assertSendToVoicemailAndRingtone(contactId1, true, "foo,bar"); // Either foo or bar
5255     }
5256 
testDoNotSendToVoicemailAfterAggregation()5257     public void testDoNotSendToVoicemailAfterAggregation() {
5258         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "e", "f");
5259         long contactId1 = queryContactId(rawContactId1);
5260         updateSendToVoicemailAndRingtone(contactId1, true, null);
5261 
5262         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "g", "h");
5263         long contactId2 = queryContactId(rawContactId2);
5264         updateSendToVoicemailAndRingtone(contactId2, false, null);
5265 
5266         // Aggregate them
5267         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
5268                 rawContactId1, rawContactId2);
5269 
5270         // Since one of the contacts had "don't send to VM" that setting wins for the aggregate
5271         assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), false, null);
5272     }
5273 
testSetSendToVoicemailAndRingtonePreservedAfterJoinAndSplit()5274     public void testSetSendToVoicemailAndRingtonePreservedAfterJoinAndSplit() {
5275         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "i", "j");
5276         long contactId1 = queryContactId(rawContactId1);
5277         updateSendToVoicemailAndRingtone(contactId1, true, "foo");
5278 
5279         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "k", "l");
5280         long contactId2 = queryContactId(rawContactId2);
5281         updateSendToVoicemailAndRingtone(contactId2, false, "bar");
5282 
5283         // Aggregate them
5284         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
5285                 rawContactId1, rawContactId2);
5286 
5287         // Split them
5288         setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
5289                 rawContactId1, rawContactId2);
5290 
5291         assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), true, "foo");
5292         assertSendToVoicemailAndRingtone(queryContactId(rawContactId2), false, "bar");
5293     }
5294 
testMarkMetadataDirtyAfterAggregation()5295     public void testMarkMetadataDirtyAfterAggregation() {
5296         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "i", "j");
5297         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "k", "l");
5298         Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
5299         Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
5300         assertDirty(rawContactUri1, true);
5301         assertDirty(rawContactUri2, true);
5302         clearDirty(rawContactUri1);
5303         clearDirty(rawContactUri2);
5304 
5305         // Aggregate them
5306         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
5307                 rawContactId1, rawContactId2);
5308 
5309         assertDirty(rawContactUri1, false);
5310         assertDirty(rawContactUri2, false);
5311         assertMetadataDirty(rawContactUri1, false);
5312         assertMetadataDirty(rawContactUri2, false);
5313         assertNetworkNotified(false);
5314     }
5315 
testStatusUpdateInsert()5316     public void testStatusUpdateInsert() {
5317         long rawContactId = RawContactUtil.createRawContact(mResolver);
5318         Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
5319         long dataId = ContentUris.parseId(imUri);
5320 
5321         ContentValues values = new ContentValues();
5322         values.put(StatusUpdates.DATA_ID, dataId);
5323         values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM);
5324         values.putNull(StatusUpdates.CUSTOM_PROTOCOL);
5325         values.put(StatusUpdates.IM_HANDLE, "aim");
5326         values.put(StatusUpdates.PRESENCE, StatusUpdates.INVISIBLE);
5327         values.put(StatusUpdates.STATUS, "Hiding");
5328         values.put(StatusUpdates.STATUS_TIMESTAMP, 100);
5329         values.put(StatusUpdates.STATUS_RES_PACKAGE, "a.b.c");
5330         values.put(StatusUpdates.STATUS_ICON, 1234);
5331         values.put(StatusUpdates.STATUS_LABEL, 2345);
5332 
5333         Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values);
5334 
5335         assertStoredValues(resultUri, values);
5336 
5337         long contactId = queryContactId(rawContactId);
5338         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
5339 
5340         values.clear();
5341         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
5342         values.put(Contacts.CONTACT_STATUS, "Hiding");
5343         values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100);
5344         values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "a.b.c");
5345         values.put(Contacts.CONTACT_STATUS_ICON, 1234);
5346         values.put(Contacts.CONTACT_STATUS_LABEL, 2345);
5347 
5348         assertStoredValues(contactUri, values);
5349 
5350         values.clear();
5351         values.put(StatusUpdates.DATA_ID, dataId);
5352         values.put(StatusUpdates.STATUS, "Cloaked");
5353         values.put(StatusUpdates.STATUS_TIMESTAMP, 200);
5354         values.put(StatusUpdates.STATUS_RES_PACKAGE, "d.e.f");
5355         values.put(StatusUpdates.STATUS_ICON, 4321);
5356         values.put(StatusUpdates.STATUS_LABEL, 5432);
5357         mResolver.insert(StatusUpdates.CONTENT_URI, values);
5358 
5359         values.clear();
5360         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
5361         values.put(Contacts.CONTACT_STATUS, "Cloaked");
5362         values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 200);
5363         values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "d.e.f");
5364         values.put(Contacts.CONTACT_STATUS_ICON, 4321);
5365         values.put(Contacts.CONTACT_STATUS_LABEL, 5432);
5366         assertStoredValues(contactUri, values);
5367     }
5368 
testStatusUpdateInferAttribution()5369     public void testStatusUpdateInferAttribution() {
5370         long rawContactId = RawContactUtil.createRawContact(mResolver);
5371         Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
5372         long dataId = ContentUris.parseId(imUri);
5373 
5374         ContentValues values = new ContentValues();
5375         values.put(StatusUpdates.DATA_ID, dataId);
5376         values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM);
5377         values.put(StatusUpdates.IM_HANDLE, "aim");
5378         values.put(StatusUpdates.STATUS, "Hiding");
5379 
5380         Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values);
5381 
5382         values.clear();
5383         values.put(StatusUpdates.DATA_ID, dataId);
5384         values.put(StatusUpdates.STATUS_LABEL, com.android.internal.R.string.imProtocolAim);
5385         values.put(StatusUpdates.STATUS, "Hiding");
5386 
5387         assertStoredValues(resultUri, values);
5388     }
5389 
testStatusUpdateMatchingImOrEmail()5390     public void testStatusUpdateMatchingImOrEmail() {
5391         long rawContactId = RawContactUtil.createRawContact(mResolver);
5392         insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
5393         insertImHandle(rawContactId, Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im");
5394         insertEmail(rawContactId, "m@acme.com");
5395 
5396         // Match on IM (standard)
5397         insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
5398                 StatusUpdates.CAPABILITY_HAS_CAMERA);
5399 
5400         // Match on IM (custom)
5401         insertStatusUpdate(Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im", StatusUpdates.IDLE, "Idle",
5402                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
5403 
5404         // Match on Email
5405         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "m@acme.com", StatusUpdates.AWAY, "Away",
5406                 StatusUpdates.CAPABILITY_HAS_VOICE);
5407 
5408         // No match
5409         insertStatusUpdate(Im.PROTOCOL_ICQ, null, "12345", StatusUpdates.DO_NOT_DISTURB, "Go away",
5410                 StatusUpdates.CAPABILITY_HAS_CAMERA);
5411 
5412         Cursor c = mResolver.query(StatusUpdates.CONTENT_URI, new String[] {
5413                 StatusUpdates.DATA_ID, StatusUpdates.PROTOCOL, StatusUpdates.CUSTOM_PROTOCOL,
5414                 StatusUpdates.PRESENCE, StatusUpdates.STATUS},
5415                 PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null, StatusUpdates.DATA_ID);
5416         assertTrue(c.moveToNext());
5417         assertStatusUpdate(c, Im.PROTOCOL_AIM, null, StatusUpdates.AVAILABLE, "Available");
5418         assertTrue(c.moveToNext());
5419         assertStatusUpdate(c, Im.PROTOCOL_CUSTOM, "my_im_proto", StatusUpdates.IDLE, "Idle");
5420         assertTrue(c.moveToNext());
5421         assertStatusUpdate(c, Im.PROTOCOL_GOOGLE_TALK, null, StatusUpdates.AWAY, "Away");
5422         assertFalse(c.moveToNext());
5423         c.close();
5424 
5425         long contactId = queryContactId(rawContactId);
5426         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
5427 
5428         ContentValues values = new ContentValues();
5429         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
5430         values.put(Contacts.CONTACT_STATUS, "Available");
5431         assertStoredValuesWithProjection(contactUri, values);
5432     }
5433 
testStatusUpdateUpdateAndDelete()5434     public void testStatusUpdateUpdateAndDelete() {
5435         long rawContactId = RawContactUtil.createRawContact(mResolver);
5436         insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
5437 
5438         long contactId = queryContactId(rawContactId);
5439         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
5440 
5441         ContentValues values = new ContentValues();
5442         values.putNull(Contacts.CONTACT_PRESENCE);
5443         values.putNull(Contacts.CONTACT_STATUS);
5444         assertStoredValuesWithProjection(contactUri, values);
5445 
5446         insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AWAY, "BUSY",
5447                 StatusUpdates.CAPABILITY_HAS_CAMERA);
5448         insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.DO_NOT_DISTURB, "GO AWAY",
5449                 StatusUpdates.CAPABILITY_HAS_CAMERA);
5450         Uri statusUri =
5451             insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
5452                     StatusUpdates.CAPABILITY_HAS_CAMERA);
5453         long statusId = ContentUris.parseId(statusUri);
5454 
5455         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
5456         values.put(Contacts.CONTACT_STATUS, "Available");
5457         assertStoredValuesWithProjection(contactUri, values);
5458 
5459         // update status_updates table to set new values for
5460         //     status_updates.status
5461         //     status_updates.status_ts
5462         //     presence
5463         long updatedTs = 200;
5464         String testUpdate = "test_update";
5465         String selection = StatusUpdates.DATA_ID + "=" + statusId;
5466         values.clear();
5467         values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
5468         values.put(StatusUpdates.STATUS, testUpdate);
5469         values.put(StatusUpdates.PRESENCE, "presence_test");
5470         mResolver.update(StatusUpdates.CONTENT_URI, values,
5471                 StatusUpdates.DATA_ID + "=" + statusId, null);
5472         assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
5473 
5474         // update status_updates table to set new values for columns in status_updates table ONLY
5475         // i.e., no rows in presence table are to be updated.
5476         updatedTs = 300;
5477         testUpdate = "test_update_new";
5478         selection = StatusUpdates.DATA_ID + "=" + statusId;
5479         values.clear();
5480         values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
5481         values.put(StatusUpdates.STATUS, testUpdate);
5482         mResolver.update(StatusUpdates.CONTENT_URI, values,
5483                 StatusUpdates.DATA_ID + "=" + statusId, null);
5484         // make sure the presence column value is still the old value
5485         values.put(StatusUpdates.PRESENCE, "presence_test");
5486         assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
5487 
5488         // update status_updates table to set new values for columns in presence table ONLY
5489         // i.e., no rows in status_updates table are to be updated.
5490         selection = StatusUpdates.DATA_ID + "=" + statusId;
5491         values.clear();
5492         values.put(StatusUpdates.PRESENCE, "presence_test_new");
5493         mResolver.update(StatusUpdates.CONTENT_URI, values,
5494                 StatusUpdates.DATA_ID + "=" + statusId, null);
5495         // make sure the status_updates table is not updated
5496         values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
5497         values.put(StatusUpdates.STATUS, testUpdate);
5498         assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
5499 
5500         // effect "delete status_updates" operation and expect the following
5501         //   data deleted from status_updates table
5502         //   presence set to null
5503         mResolver.delete(StatusUpdates.CONTENT_URI, StatusUpdates.DATA_ID + "=" + statusId, null);
5504         values.clear();
5505         values.putNull(Contacts.CONTACT_PRESENCE);
5506         assertStoredValuesWithProjection(contactUri, values);
5507     }
5508 
testStatusUpdateUpdateToNull()5509     public void testStatusUpdateUpdateToNull() {
5510         long rawContactId = RawContactUtil.createRawContact(mResolver);
5511         insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
5512 
5513         long contactId = queryContactId(rawContactId);
5514         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
5515 
5516         ContentValues values = new ContentValues();
5517         Uri statusUri =
5518             insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
5519                     StatusUpdates.CAPABILITY_HAS_CAMERA);
5520         long statusId = ContentUris.parseId(statusUri);
5521 
5522         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
5523         values.put(Contacts.CONTACT_STATUS, "Available");
5524         assertStoredValuesWithProjection(contactUri, values);
5525 
5526         values.clear();
5527         values.putNull(StatusUpdates.PRESENCE);
5528         mResolver.update(StatusUpdates.CONTENT_URI, values,
5529                 StatusUpdates.DATA_ID + "=" + statusId, null);
5530 
5531         values.clear();
5532         values.putNull(Contacts.CONTACT_PRESENCE);
5533         values.put(Contacts.CONTACT_STATUS, "Available");
5534         assertStoredValuesWithProjection(contactUri, values);
5535     }
5536 
testStatusUpdateWithTimestamp()5537     public void testStatusUpdateWithTimestamp() {
5538         long rawContactId = RawContactUtil.createRawContact(mResolver);
5539         insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
5540         insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
5541 
5542         long contactId = queryContactId(rawContactId);
5543         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
5544         insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Offline", 80,
5545                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
5546         insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Available", 100,
5547                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
5548         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", 0, "Busy", 90,
5549                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
5550 
5551         // Should return the latest status
5552         ContentValues values = new ContentValues();
5553         values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100);
5554         values.put(Contacts.CONTACT_STATUS, "Available");
5555         assertStoredValuesWithProjection(contactUri, values);
5556     }
5557 
assertStatusUpdate(Cursor c, int protocol, String customProtocol, int presence, String status)5558     private void assertStatusUpdate(Cursor c, int protocol, String customProtocol, int presence,
5559             String status) {
5560         ContentValues values = new ContentValues();
5561         values.put(StatusUpdates.PROTOCOL, protocol);
5562         values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
5563         values.put(StatusUpdates.PRESENCE, presence);
5564         values.put(StatusUpdates.STATUS, status);
5565         assertCursorValues(c, values);
5566     }
5567 
5568     // Stream item query test cases.
5569 
testQueryStreamItemsByRawContactId()5570     public void testQueryStreamItemsByRawContactId() {
5571         long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
5572         ContentValues values = buildGenericStreamItemValues();
5573         insertStreamItem(rawContactId, values, mAccount);
5574         assertStoredValues(
5575                 Uri.withAppendedPath(
5576                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
5577                         RawContacts.StreamItems.CONTENT_DIRECTORY),
5578                 values);
5579     }
5580 
testQueryStreamItemsByContactId()5581     public void testQueryStreamItemsByContactId() {
5582         long rawContactId = RawContactUtil.createRawContact(mResolver);
5583         long contactId = queryContactId(rawContactId);
5584         ContentValues values = buildGenericStreamItemValues();
5585         insertStreamItem(rawContactId, values, null);
5586         assertStoredValues(
5587                 Uri.withAppendedPath(
5588                         ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
5589                         Contacts.StreamItems.CONTENT_DIRECTORY),
5590                 values);
5591     }
5592 
testQueryStreamItemsByLookupKey()5593     public void testQueryStreamItemsByLookupKey() {
5594         long rawContactId = RawContactUtil.createRawContact(mResolver);
5595         long contactId = queryContactId(rawContactId);
5596         String lookupKey = queryLookupKey(contactId);
5597         ContentValues values = buildGenericStreamItemValues();
5598         insertStreamItem(rawContactId, values, null);
5599         assertStoredValues(
5600                 Uri.withAppendedPath(
5601                         Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
5602                         Contacts.StreamItems.CONTENT_DIRECTORY),
5603                 values);
5604     }
5605 
testQueryStreamItemsByLookupKeyAndContactId()5606     public void testQueryStreamItemsByLookupKeyAndContactId() {
5607         long rawContactId = RawContactUtil.createRawContact(mResolver);
5608         long contactId = queryContactId(rawContactId);
5609         String lookupKey = queryLookupKey(contactId);
5610         ContentValues values = buildGenericStreamItemValues();
5611         insertStreamItem(rawContactId, values, null);
5612         assertStoredValues(
5613                 Uri.withAppendedPath(
5614                         ContentUris.withAppendedId(
5615                                 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
5616                                 contactId),
5617                         Contacts.StreamItems.CONTENT_DIRECTORY),
5618                 values);
5619     }
5620 
testQueryStreamItems()5621     public void testQueryStreamItems() {
5622         long rawContactId = RawContactUtil.createRawContact(mResolver);
5623         ContentValues values = buildGenericStreamItemValues();
5624         insertStreamItem(rawContactId, values, null);
5625         assertStoredValues(StreamItems.CONTENT_URI, values);
5626     }
5627 
testQueryStreamItemsWithSelection()5628     public void testQueryStreamItemsWithSelection() {
5629         long rawContactId = RawContactUtil.createRawContact(mResolver);
5630         ContentValues firstValues = buildGenericStreamItemValues();
5631         insertStreamItem(rawContactId, firstValues, null);
5632 
5633         ContentValues secondValues = buildGenericStreamItemValues();
5634         secondValues.put(StreamItems.TEXT, "Goodbye world");
5635         insertStreamItem(rawContactId, secondValues, null);
5636 
5637         // Select only the first stream item.
5638         assertStoredValues(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?",
5639                 new String[]{"Hello world"}, firstValues);
5640 
5641         // Select only the second stream item.
5642         assertStoredValues(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?",
5643                 new String[]{"Goodbye world"}, secondValues);
5644     }
5645 
testQueryStreamItemById()5646     public void testQueryStreamItemById() {
5647         long rawContactId = RawContactUtil.createRawContact(mResolver);
5648         ContentValues firstValues = buildGenericStreamItemValues();
5649         Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
5650         long firstStreamItemId = ContentUris.parseId(resultUri);
5651 
5652         ContentValues secondValues = buildGenericStreamItemValues();
5653         secondValues.put(StreamItems.TEXT, "Goodbye world");
5654         resultUri = insertStreamItem(rawContactId, secondValues, null);
5655         long secondStreamItemId = ContentUris.parseId(resultUri);
5656 
5657         // Select only the first stream item.
5658         assertStoredValues(ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId),
5659                 firstValues);
5660 
5661         // Select only the second stream item.
5662         assertStoredValues(ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId),
5663                 secondValues);
5664     }
5665 
5666     // Stream item photo insertion + query test cases.
5667 
testQueryStreamItemPhotoWithSelection()5668     public void testQueryStreamItemPhotoWithSelection() {
5669         long rawContactId = RawContactUtil.createRawContact(mResolver);
5670         ContentValues values = buildGenericStreamItemValues();
5671         Uri resultUri = insertStreamItem(rawContactId, values, null);
5672         long streamItemId = ContentUris.parseId(resultUri);
5673 
5674         ContentValues photo1Values = buildGenericStreamItemPhotoValues(1);
5675         insertStreamItemPhoto(streamItemId, photo1Values, null);
5676         photo1Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
5677         ContentValues photo2Values = buildGenericStreamItemPhotoValues(2);
5678         insertStreamItemPhoto(streamItemId, photo2Values, null);
5679 
5680         // Select only the first photo.
5681         assertStoredValues(StreamItems.CONTENT_PHOTO_URI, StreamItemPhotos.SORT_INDEX + "=?",
5682                 new String[]{"1"}, photo1Values);
5683     }
5684 
testQueryStreamItemPhotoByStreamItemId()5685     public void testQueryStreamItemPhotoByStreamItemId() {
5686         long rawContactId = RawContactUtil.createRawContact(mResolver);
5687 
5688         // Insert a first stream item.
5689         ContentValues firstValues = buildGenericStreamItemValues();
5690         Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
5691         long firstStreamItemId = ContentUris.parseId(resultUri);
5692 
5693         // Insert a second stream item.
5694         ContentValues secondValues = buildGenericStreamItemValues();
5695         resultUri = insertStreamItem(rawContactId, secondValues, null);
5696         long secondStreamItemId = ContentUris.parseId(resultUri);
5697 
5698         // Add a photo to the first stream item.
5699         ContentValues photo1Values = buildGenericStreamItemPhotoValues(1);
5700         insertStreamItemPhoto(firstStreamItemId, photo1Values, null);
5701         photo1Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
5702 
5703         // Add a photo to the second stream item.
5704         ContentValues photo2Values = buildGenericStreamItemPhotoValues(1);
5705         photo2Values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
5706                 R.drawable.nebula, PhotoSize.ORIGINAL));
5707         insertStreamItemPhoto(secondStreamItemId, photo2Values, null);
5708         photo2Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
5709 
5710         // Select only the photos from the second stream item.
5711         assertStoredValues(Uri.withAppendedPath(
5712                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId),
5713                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), photo2Values);
5714     }
5715 
testQueryStreamItemPhotoByStreamItemPhotoId()5716     public void testQueryStreamItemPhotoByStreamItemPhotoId() {
5717         long rawContactId = RawContactUtil.createRawContact(mResolver);
5718 
5719         // Insert a first stream item.
5720         ContentValues firstValues = buildGenericStreamItemValues();
5721         Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
5722         long firstStreamItemId = ContentUris.parseId(resultUri);
5723 
5724         // Insert a second stream item.
5725         ContentValues secondValues = buildGenericStreamItemValues();
5726         resultUri = insertStreamItem(rawContactId, secondValues, null);
5727         long secondStreamItemId = ContentUris.parseId(resultUri);
5728 
5729         // Add a photo to the first stream item.
5730         ContentValues photo1Values = buildGenericStreamItemPhotoValues(1);
5731         resultUri = insertStreamItemPhoto(firstStreamItemId, photo1Values, null);
5732         long firstPhotoId = ContentUris.parseId(resultUri);
5733         photo1Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
5734 
5735         // Add a photo to the second stream item.
5736         ContentValues photo2Values = buildGenericStreamItemPhotoValues(1);
5737         photo2Values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
5738                 R.drawable.galaxy, PhotoSize.ORIGINAL));
5739         resultUri = insertStreamItemPhoto(secondStreamItemId, photo2Values, null);
5740         long secondPhotoId = ContentUris.parseId(resultUri);
5741         photo2Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
5742 
5743         // Select the first photo.
5744         assertStoredValues(ContentUris.withAppendedId(
5745                 Uri.withAppendedPath(
5746                         ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId),
5747                         StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
5748                 firstPhotoId),
5749                 photo1Values);
5750 
5751         // Select the second photo.
5752         assertStoredValues(ContentUris.withAppendedId(
5753                 Uri.withAppendedPath(
5754                         ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId),
5755                         StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
5756                 secondPhotoId),
5757                 photo2Values);
5758     }
5759 
5760     // Stream item insertion test cases.
5761 
testInsertStreamItemInProfileRequiresWriteProfileAccess()5762     public void testInsertStreamItemInProfileRequiresWriteProfileAccess() {
5763         long profileRawContactId = createBasicProfileContact(new ContentValues());
5764 
5765         // Try inserting a stream item. It should still succeed even without the profile permission.
5766         ContentValues values = buildGenericStreamItemValues();
5767         insertStreamItem(profileRawContactId, values, null);
5768     }
5769 
testInsertStreamItemWithContentValues()5770     public void testInsertStreamItemWithContentValues() {
5771         long rawContactId = RawContactUtil.createRawContact(mResolver);
5772         ContentValues values = buildGenericStreamItemValues();
5773         values.put(StreamItems.RAW_CONTACT_ID, rawContactId);
5774         mResolver.insert(StreamItems.CONTENT_URI, values);
5775         assertStoredValues(Uri.withAppendedPath(
5776                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
5777                 RawContacts.StreamItems.CONTENT_DIRECTORY), values);
5778     }
5779 
testInsertStreamItemOverLimit()5780     public void testInsertStreamItemOverLimit() {
5781         long rawContactId = RawContactUtil.createRawContact(mResolver);
5782         ContentValues values = buildGenericStreamItemValues();
5783         values.put(StreamItems.RAW_CONTACT_ID, rawContactId);
5784 
5785         List<Long> streamItemIds = Lists.newArrayList();
5786 
5787         // Insert MAX + 1 stream items.
5788         long baseTime = System.currentTimeMillis();
5789         for (int i = 0; i < 6; i++) {
5790             values.put(StreamItems.TIMESTAMP, baseTime + i);
5791             Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values);
5792             streamItemIds.add(ContentUris.parseId(resultUri));
5793         }
5794         Long doomedStreamItemId = streamItemIds.get(0);
5795 
5796         // There should only be MAX items.  The oldest one should have been cleaned up.
5797         Cursor c = mResolver.query(
5798                 Uri.withAppendedPath(
5799                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
5800                         RawContacts.StreamItems.CONTENT_DIRECTORY),
5801                 new String[]{StreamItems._ID}, null, null, null);
5802         try {
5803             while(c.moveToNext()) {
5804                 long streamItemId = c.getLong(0);
5805                 streamItemIds.remove(streamItemId);
5806             }
5807         } finally {
5808             c.close();
5809         }
5810 
5811         assertEquals(1, streamItemIds.size());
5812     }
5813 
testInsertStreamItemOlderThanOldestInLimit()5814     public void testInsertStreamItemOlderThanOldestInLimit() {
5815         long rawContactId = RawContactUtil.createRawContact(mResolver);
5816         ContentValues values = buildGenericStreamItemValues();
5817         values.put(StreamItems.RAW_CONTACT_ID, rawContactId);
5818 
5819         // Insert MAX stream items.
5820         long baseTime = System.currentTimeMillis();
5821         for (int i = 0; i < 5; i++) {
5822             values.put(StreamItems.TIMESTAMP, baseTime + i);
5823             Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values);
5824             assertNotSame("Expected non-0 stream item ID to be inserted",
5825                     0L, ContentUris.parseId(resultUri));
5826         }
5827 
5828         // Now try to insert a stream item that's older.  It should be deleted immediately
5829         // and return an ID of 0.
5830         values.put(StreamItems.TIMESTAMP, baseTime - 1);
5831         Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values);
5832         assertEquals(0L, ContentUris.parseId(resultUri));
5833     }
5834 
5835     // Stream item photo insertion test cases.
5836 
testInsertStreamItemsAndPhotosInBatch()5837     public void testInsertStreamItemsAndPhotosInBatch() throws Exception {
5838         long rawContactId = RawContactUtil.createRawContact(mResolver);
5839         ContentValues streamItemValues = buildGenericStreamItemValues();
5840         ContentValues streamItemPhotoValues = buildGenericStreamItemPhotoValues(0);
5841 
5842         ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
5843         ops.add(ContentProviderOperation.newInsert(
5844                 Uri.withAppendedPath(
5845                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
5846                         RawContacts.StreamItems.CONTENT_DIRECTORY))
5847                 .withValues(streamItemValues).build());
5848         for (int i = 0; i < 5; i++) {
5849             streamItemPhotoValues.put(StreamItemPhotos.SORT_INDEX, i);
5850             ops.add(ContentProviderOperation.newInsert(StreamItems.CONTENT_PHOTO_URI)
5851                     .withValues(streamItemPhotoValues)
5852                     .withValueBackReference(StreamItemPhotos.STREAM_ITEM_ID, 0)
5853                     .build());
5854         }
5855         mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
5856 
5857         // Check that all five photos were inserted under the raw contact.
5858         Cursor c = mResolver.query(StreamItems.CONTENT_URI, new String[]{StreamItems._ID},
5859                 StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
5860                 null);
5861         long streamItemId = 0;
5862         try {
5863             assertEquals(1, c.getCount());
5864             c.moveToFirst();
5865             streamItemId = c.getLong(0);
5866         } finally {
5867             c.close();
5868         }
5869 
5870         c = mResolver.query(Uri.withAppendedPath(
5871                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
5872                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
5873                 new String[]{StreamItemPhotos._ID, StreamItemPhotos.PHOTO_URI},
5874                 null, null, null);
5875         try {
5876             assertEquals(5, c.getCount());
5877             byte[] expectedPhotoBytes = loadPhotoFromResource(
5878                     R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO);
5879             while (c.moveToNext()) {
5880                 String photoUri = c.getString(1);
5881                 EvenMoreAsserts.assertImageRawData(getContext(),
5882                         expectedPhotoBytes, mResolver.openInputStream(Uri.parse(photoUri)));
5883             }
5884         } finally {
5885             c.close();
5886         }
5887     }
5888 
5889     // Stream item update test cases.
5890 
testUpdateStreamItemById()5891     public void testUpdateStreamItemById() {
5892         long rawContactId = RawContactUtil.createRawContact(mResolver);
5893         ContentValues values = buildGenericStreamItemValues();
5894         Uri resultUri = insertStreamItem(rawContactId, values, null);
5895         long streamItemId = ContentUris.parseId(resultUri);
5896         values.put(StreamItems.TEXT, "Goodbye world");
5897         mResolver.update(ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), values,
5898                 null, null);
5899         assertStoredValues(Uri.withAppendedPath(
5900                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
5901                 RawContacts.StreamItems.CONTENT_DIRECTORY), values);
5902     }
5903 
testUpdateStreamItemWithContentValues()5904     public void testUpdateStreamItemWithContentValues() {
5905         long rawContactId = RawContactUtil.createRawContact(mResolver);
5906         ContentValues values = buildGenericStreamItemValues();
5907         Uri resultUri = insertStreamItem(rawContactId, values, null);
5908         long streamItemId = ContentUris.parseId(resultUri);
5909         values.put(StreamItems._ID, streamItemId);
5910         values.put(StreamItems.TEXT, "Goodbye world");
5911         mResolver.update(StreamItems.CONTENT_URI, values, null, null);
5912         assertStoredValues(Uri.withAppendedPath(
5913                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
5914                 RawContacts.StreamItems.CONTENT_DIRECTORY), values);
5915     }
5916 
5917     // Stream item photo update test cases.
5918 
testUpdateStreamItemPhotoById()5919     public void testUpdateStreamItemPhotoById() throws IOException {
5920         long rawContactId = RawContactUtil.createRawContact(mResolver);
5921         ContentValues values = buildGenericStreamItemValues();
5922         Uri resultUri = insertStreamItem(rawContactId, values, null);
5923         long streamItemId = ContentUris.parseId(resultUri);
5924         ContentValues photoValues = buildGenericStreamItemPhotoValues(1);
5925         resultUri = insertStreamItemPhoto(streamItemId, photoValues, null);
5926         long streamItemPhotoId = ContentUris.parseId(resultUri);
5927 
5928         photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
5929                 R.drawable.nebula, PhotoSize.ORIGINAL));
5930         Uri photoUri =
5931                 ContentUris.withAppendedId(
5932                         Uri.withAppendedPath(
5933                                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
5934                                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
5935                         streamItemPhotoId);
5936         mResolver.update(photoUri, photoValues, null, null);
5937         photoValues.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
5938         assertStoredValues(photoUri, photoValues);
5939 
5940         // Check that the photo stored is the expected one.
5941         String displayPhotoUri = getStoredValue(photoUri, StreamItemPhotos.PHOTO_URI);
5942         EvenMoreAsserts.assertImageRawData(getContext(),
5943                 loadPhotoFromResource(R.drawable.nebula, PhotoSize.DISPLAY_PHOTO),
5944                 mResolver.openInputStream(Uri.parse(displayPhotoUri)));
5945     }
5946 
testUpdateStreamItemPhotoWithContentValues()5947     public void testUpdateStreamItemPhotoWithContentValues() throws IOException {
5948         long rawContactId = RawContactUtil.createRawContact(mResolver);
5949         ContentValues values = buildGenericStreamItemValues();
5950         Uri resultUri = insertStreamItem(rawContactId, values, null);
5951         long streamItemId = ContentUris.parseId(resultUri);
5952         ContentValues photoValues = buildGenericStreamItemPhotoValues(1);
5953         resultUri = insertStreamItemPhoto(streamItemId, photoValues, null);
5954         long streamItemPhotoId = ContentUris.parseId(resultUri);
5955 
5956         photoValues.put(StreamItemPhotos._ID, streamItemPhotoId);
5957         photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
5958                 R.drawable.nebula, PhotoSize.ORIGINAL));
5959         Uri photoUri =
5960                 Uri.withAppendedPath(
5961                         ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
5962                         StreamItems.StreamItemPhotos.CONTENT_DIRECTORY);
5963         mResolver.update(photoUri, photoValues, null, null);
5964         photoValues.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
5965         assertStoredValues(photoUri, photoValues);
5966 
5967         // Check that the photo stored is the expected one.
5968         String displayPhotoUri = getStoredValue(photoUri, StreamItemPhotos.PHOTO_URI);
5969         EvenMoreAsserts.assertImageRawData(getContext(),
5970                 loadPhotoFromResource(R.drawable.nebula, PhotoSize.DISPLAY_PHOTO),
5971                 mResolver.openInputStream(Uri.parse(displayPhotoUri)));
5972     }
5973 
5974     // Stream item deletion test cases.
5975 
testDeleteStreamItemById()5976     public void testDeleteStreamItemById() {
5977         long rawContactId = RawContactUtil.createRawContact(mResolver);
5978         ContentValues firstValues = buildGenericStreamItemValues();
5979         Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
5980         long firstStreamItemId = ContentUris.parseId(resultUri);
5981 
5982         ContentValues secondValues = buildGenericStreamItemValues();
5983         secondValues.put(StreamItems.TEXT, "Goodbye world");
5984         insertStreamItem(rawContactId, secondValues, null);
5985 
5986         // Delete the first stream item.
5987         mResolver.delete(ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId),
5988                 null, null);
5989 
5990         // Check that only the second item remains.
5991         assertStoredValues(Uri.withAppendedPath(
5992                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
5993                 RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues);
5994     }
5995 
testDeleteStreamItemWithSelection()5996     public void testDeleteStreamItemWithSelection() {
5997         long rawContactId = RawContactUtil.createRawContact(mResolver);
5998         ContentValues firstValues = buildGenericStreamItemValues();
5999         insertStreamItem(rawContactId, firstValues, null);
6000 
6001         ContentValues secondValues = buildGenericStreamItemValues();
6002         secondValues.put(StreamItems.TEXT, "Goodbye world");
6003         insertStreamItem(rawContactId, secondValues, null);
6004 
6005         // Delete the first stream item with a custom selection.
6006         mResolver.delete(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?",
6007                 new String[]{"Hello world"});
6008 
6009         // Check that only the second item remains.
6010         assertStoredValues(Uri.withAppendedPath(
6011                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
6012                 RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues);
6013     }
6014 
6015     // Stream item photo deletion test cases.
6016 
testDeleteStreamItemPhotoById()6017     public void testDeleteStreamItemPhotoById() {
6018         long rawContactId = RawContactUtil.createRawContact(mResolver);
6019         long streamItemId = ContentUris.parseId(
6020                 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null));
6021         long streamItemPhotoId = ContentUris.parseId(
6022                 insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), null));
6023         mResolver.delete(
6024                 ContentUris.withAppendedId(
6025                         Uri.withAppendedPath(
6026                                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
6027                                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
6028                         streamItemPhotoId), null, null);
6029 
6030         Cursor c = mResolver.query(StreamItems.CONTENT_PHOTO_URI,
6031                 new String[]{StreamItemPhotos._ID},
6032                 StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{String.valueOf(streamItemId)},
6033                 null);
6034         try {
6035             assertEquals("Expected photo to be deleted.", 0, c.getCount());
6036         } finally {
6037             c.close();
6038         }
6039     }
6040 
testDeleteStreamItemPhotoWithSelection()6041     public void testDeleteStreamItemPhotoWithSelection() {
6042         long rawContactId = RawContactUtil.createRawContact(mResolver);
6043         long streamItemId = ContentUris.parseId(
6044                 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null));
6045         ContentValues firstPhotoValues = buildGenericStreamItemPhotoValues(0);
6046         ContentValues secondPhotoValues = buildGenericStreamItemPhotoValues(1);
6047         insertStreamItemPhoto(streamItemId, firstPhotoValues, null);
6048         firstPhotoValues.remove(StreamItemPhotos.PHOTO);  // Removed while processing.
6049         insertStreamItemPhoto(streamItemId, secondPhotoValues, null);
6050         Uri photoUri = Uri.withAppendedPath(
6051                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
6052                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY);
6053         mResolver.delete(photoUri, StreamItemPhotos.SORT_INDEX + "=1", null);
6054 
6055         assertStoredValues(photoUri, firstPhotoValues);
6056     }
6057 
testDeleteStreamItemsWhenRawContactDeleted()6058     public void testDeleteStreamItemsWhenRawContactDeleted() {
6059         long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6060         Uri streamItemUri = insertStreamItem(rawContactId,
6061                 buildGenericStreamItemValues(), mAccount);
6062         Uri streamItemPhotoUri = insertStreamItemPhoto(ContentUris.parseId(streamItemUri),
6063                         buildGenericStreamItemPhotoValues(0), mAccount);
6064         mResolver.delete(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
6065                 null, null);
6066 
6067         ContentValues[] emptyValues = new ContentValues[0];
6068 
6069         // The stream item and its photo should be gone.
6070         assertStoredValues(streamItemUri, emptyValues);
6071         assertStoredValues(streamItemPhotoUri, emptyValues);
6072     }
6073 
testQueryStreamItemLimit()6074     public void testQueryStreamItemLimit() {
6075         ContentValues values = new ContentValues();
6076         values.put(StreamItems.MAX_ITEMS, 5);
6077         assertStoredValues(StreamItems.CONTENT_LIMIT_URI, values);
6078     }
6079 
6080     // Tests for inserting or updating stream items as a side-effect of making status updates
6081     // (forward-compatibility of status updates into the new social stream API).
6082 
testStreamItemInsertedOnStatusUpdate()6083     public void testStreamItemInsertedOnStatusUpdate() {
6084 
6085         // This method of creating a raw contact automatically inserts a status update with
6086         // the status message "hacking".
6087         ContentValues values = new ContentValues();
6088         long rawContactId = createRawContact(values, "18004664411",
6089                 "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
6090                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
6091                         StatusUpdates.CAPABILITY_HAS_VOICE);
6092 
6093         ContentValues expectedValues = new ContentValues();
6094         expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
6095         expectedValues.put(StreamItems.TEXT, "hacking");
6096         assertStoredValues(RawContacts.CONTENT_URI.buildUpon()
6097                 .appendPath(String.valueOf(rawContactId))
6098                 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(),
6099                 expectedValues);
6100     }
6101 
testStreamItemInsertedOnStatusUpdate_HtmlQuoting()6102     public void testStreamItemInsertedOnStatusUpdate_HtmlQuoting() {
6103 
6104         // This method of creating a raw contact automatically inserts a status update with
6105         // the status message "hacking".
6106         ContentValues values = new ContentValues();
6107         long rawContactId = createRawContact(values, "18004664411",
6108                 "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
6109                 StatusUpdates.CAPABILITY_HAS_VOICE);
6110 
6111         // Insert a new status update for the raw contact.
6112         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411@acme.com",
6113                 StatusUpdates.INVISIBLE, "& <b> test &#39;", StatusUpdates.CAPABILITY_HAS_VOICE);
6114 
6115         ContentValues expectedValues = new ContentValues();
6116         expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
6117         expectedValues.put(StreamItems.TEXT, "&amp; &lt;b&gt; test &amp;#39;");
6118         assertStoredValues(RawContacts.CONTENT_URI.buildUpon()
6119                 .appendPath(String.valueOf(rawContactId))
6120                 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(),
6121                 expectedValues);
6122     }
6123 
testStreamItemUpdatedOnSecondStatusUpdate()6124     public void testStreamItemUpdatedOnSecondStatusUpdate() {
6125 
6126         // This method of creating a raw contact automatically inserts a status update with
6127         // the status message "hacking".
6128         ContentValues values = new ContentValues();
6129         int chatMode = StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
6130                 StatusUpdates.CAPABILITY_HAS_VOICE;
6131         long rawContactId = createRawContact(values, "18004664411",
6132                 "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, chatMode);
6133 
6134         // Insert a new status update for the raw contact.
6135         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411@acme.com",
6136                 StatusUpdates.INVISIBLE, "finished hacking", chatMode);
6137 
6138         ContentValues expectedValues = new ContentValues();
6139         expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
6140         expectedValues.put(StreamItems.TEXT, "finished hacking");
6141         assertStoredValues(RawContacts.CONTENT_URI.buildUpon()
6142                 .appendPath(String.valueOf(rawContactId))
6143                 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(),
6144                 expectedValues);
6145     }
6146 
buildGenericStreamItemValues()6147     private ContentValues buildGenericStreamItemValues() {
6148         ContentValues values = new ContentValues();
6149         values.put(StreamItems.TEXT, "Hello world");
6150         values.put(StreamItems.TIMESTAMP, System.currentTimeMillis());
6151         values.put(StreamItems.COMMENTS, "Reshared by 123 others");
6152         return values;
6153     }
6154 
buildGenericStreamItemPhotoValues(int sortIndex)6155     private ContentValues buildGenericStreamItemPhotoValues(int sortIndex) {
6156         ContentValues values = new ContentValues();
6157         values.put(StreamItemPhotos.SORT_INDEX, sortIndex);
6158         values.put(StreamItemPhotos.PHOTO,
6159                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.ORIGINAL));
6160         return values;
6161     }
6162 
testSingleStatusUpdateRowPerContact()6163     public void testSingleStatusUpdateRowPerContact() {
6164         int protocol1 = Im.PROTOCOL_GOOGLE_TALK;
6165         String handle1 = "test@gmail.com";
6166 
6167         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
6168         insertImHandle(rawContactId1, protocol1, null, handle1);
6169 
6170         insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AVAILABLE, "Green",
6171                 StatusUpdates.CAPABILITY_HAS_CAMERA);
6172         insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AWAY, "Yellow",
6173                 StatusUpdates.CAPABILITY_HAS_CAMERA);
6174         insertStatusUpdate(protocol1, null, handle1, StatusUpdates.INVISIBLE, "Red",
6175                 StatusUpdates.CAPABILITY_HAS_CAMERA);
6176 
6177         Cursor c = queryContact(queryContactId(rawContactId1),
6178                 new String[] {Contacts.CONTACT_PRESENCE, Contacts.CONTACT_STATUS});
6179         assertEquals(1, c.getCount());
6180 
6181         c.moveToFirst();
6182         assertEquals(StatusUpdates.INVISIBLE, c.getInt(0));
6183         assertEquals("Red", c.getString(1));
6184         c.close();
6185     }
6186 
updateSendToVoicemailAndRingtone(long contactId, boolean sendToVoicemail, String ringtone)6187     private void updateSendToVoicemailAndRingtone(long contactId, boolean sendToVoicemail,
6188             String ringtone) {
6189         ContentValues values = new ContentValues();
6190         values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail);
6191         if (ringtone != null) {
6192             values.put(Contacts.CUSTOM_RINGTONE, ringtone);
6193         }
6194 
6195         final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
6196         int count = mResolver.update(uri, values, null, null);
6197         assertEquals(1, count);
6198     }
6199 
updateSendToVoicemailAndRingtoneWithSelection(long contactId, boolean sendToVoicemail, String ringtone)6200     private void updateSendToVoicemailAndRingtoneWithSelection(long contactId,
6201             boolean sendToVoicemail, String ringtone) {
6202         ContentValues values = new ContentValues();
6203         values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail);
6204         if (ringtone != null) {
6205             values.put(Contacts.CUSTOM_RINGTONE, ringtone);
6206         }
6207 
6208         int count = mResolver.update(Contacts.CONTENT_URI, values, Contacts._ID + "=" + contactId,
6209                 null);
6210         assertEquals(1, count);
6211     }
6212 
assertSendToVoicemailAndRingtone(long contactId, boolean expectedSendToVoicemail, String expectedRingtone)6213     private void assertSendToVoicemailAndRingtone(long contactId, boolean expectedSendToVoicemail,
6214             String expectedRingtone) {
6215         Cursor c = queryContact(contactId);
6216         assertTrue(c.moveToNext());
6217         int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL));
6218         assertEquals(expectedSendToVoicemail ? 1 : 0, sendToVoicemail);
6219         String ringtone = c.getString(c.getColumnIndex(Contacts.CUSTOM_RINGTONE));
6220         if (expectedRingtone == null) {
6221             assertNull(ringtone);
6222         } else {
6223             assertTrue(ArrayUtils.contains(expectedRingtone.split(","), ringtone));
6224         }
6225         c.close();
6226     }
6227 
testContactVisibilityUpdateOnMembershipChange()6228     public void testContactVisibilityUpdateOnMembershipChange() {
6229         long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6230         assertVisibility(rawContactId, "0");
6231 
6232         long visibleGroupId = createGroup(mAccount, "123", "Visible", 1);
6233         long invisibleGroupId = createGroup(mAccount, "567", "Invisible", 0);
6234 
6235         Uri membership1 = insertGroupMembership(rawContactId, visibleGroupId);
6236         assertVisibility(rawContactId, "1");
6237 
6238         Uri membership2 = insertGroupMembership(rawContactId, invisibleGroupId);
6239         assertVisibility(rawContactId, "1");
6240 
6241         mResolver.delete(membership1, null, null);
6242         assertVisibility(rawContactId, "0");
6243 
6244         ContentValues values = new ContentValues();
6245         values.put(GroupMembership.GROUP_ROW_ID, visibleGroupId);
6246 
6247         mResolver.update(membership2, values, null, null);
6248         assertVisibility(rawContactId, "1");
6249     }
6250 
assertVisibility(long rawContactId, String expectedValue)6251     private void assertVisibility(long rawContactId, String expectedValue) {
6252         assertStoredValue(Contacts.CONTENT_URI, Contacts._ID + "=" + queryContactId(rawContactId),
6253                 null, Contacts.IN_VISIBLE_GROUP, expectedValue);
6254     }
6255 
testSupplyingBothValuesAndParameters()6256     public void testSupplyingBothValuesAndParameters() throws Exception {
6257         Account account = new Account("account 1", "type%/:1");
6258         Uri uri = ContactsContract.Groups.CONTENT_URI.buildUpon()
6259                 .appendQueryParameter(ContactsContract.Groups.ACCOUNT_NAME, account.name)
6260                 .appendQueryParameter(ContactsContract.Groups.ACCOUNT_TYPE, account.type)
6261                 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
6262                 .build();
6263 
6264         ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(uri);
6265         builder.withValue(ContactsContract.Groups.ACCOUNT_TYPE, account.type);
6266         builder.withValue(ContactsContract.Groups.ACCOUNT_NAME, account.name);
6267         builder.withValue(ContactsContract.Groups.SYSTEM_ID, "some id");
6268         builder.withValue(ContactsContract.Groups.TITLE, "some name");
6269         builder.withValue(ContactsContract.Groups.GROUP_VISIBLE, 1);
6270 
6271         mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(builder.build()));
6272 
6273         builder = ContentProviderOperation.newInsert(uri);
6274         builder.withValue(ContactsContract.Groups.ACCOUNT_TYPE, account.type + "diff");
6275         builder.withValue(ContactsContract.Groups.ACCOUNT_NAME, account.name);
6276         builder.withValue(ContactsContract.Groups.SYSTEM_ID, "some other id");
6277         builder.withValue(ContactsContract.Groups.TITLE, "some other name");
6278         builder.withValue(ContactsContract.Groups.GROUP_VISIBLE, 1);
6279 
6280         try {
6281             mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(builder.build()));
6282             fail("Expected IllegalArgumentException");
6283         } catch (IllegalArgumentException ex) {
6284             // Expected
6285         }
6286     }
6287 
testContentEntityIterator()6288     public void testContentEntityIterator() {
6289         // create multiple contacts and check that the selected ones are returned
6290         long id;
6291 
6292         long groupId1 = createGroup(mAccount, "gsid1", "title1");
6293         long groupId2 = createGroup(mAccount, "gsid2", "title2");
6294 
6295         id = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.SOURCE_ID, "c0");
6296         insertGroupMembership(id, "gsid1");
6297         insertEmail(id, "c0@email.com");
6298         insertPhoneNumber(id, "5551212c0");
6299 
6300         long c1 = id = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.SOURCE_ID,
6301                 "c1");
6302         Uri id_1_0 = insertGroupMembership(id, "gsid1");
6303         Uri id_1_1 = insertGroupMembership(id, "gsid2");
6304         Uri id_1_2 = insertEmail(id, "c1@email.com");
6305         Uri id_1_3 = insertPhoneNumber(id, "5551212c1");
6306 
6307         long c2 = id = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.SOURCE_ID,
6308                 "c2");
6309         Uri id_2_0 = insertGroupMembership(id, "gsid1");
6310         Uri id_2_1 = insertEmail(id, "c2@email.com");
6311         Uri id_2_2 = insertPhoneNumber(id, "5551212c2");
6312 
6313         long c3 = id = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.SOURCE_ID,
6314                 "c3");
6315         Uri id_3_0 = insertGroupMembership(id, groupId2);
6316         Uri id_3_1 = insertEmail(id, "c3@email.com");
6317         Uri id_3_2 = insertPhoneNumber(id, "5551212c3");
6318 
6319         EntityIterator iterator = RawContacts.newEntityIterator(mResolver.query(
6320                 TestUtil.maybeAddAccountQueryParameters(RawContactsEntity.CONTENT_URI, mAccount),
6321                 null, RawContacts.SOURCE_ID + " in ('c1', 'c2', 'c3')", null, null));
6322         Entity entity;
6323         ContentValues[] subValues;
6324         entity = iterator.next();
6325         assertEquals(c1, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
6326         subValues = asSortedContentValuesArray(entity.getSubValues());
6327         assertEquals(4, subValues.length);
6328         assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
6329                 Data._ID, id_1_0,
6330                 GroupMembership.GROUP_ROW_ID, groupId1,
6331                 GroupMembership.GROUP_SOURCE_ID, "gsid1");
6332         assertDataRow(subValues[1], GroupMembership.CONTENT_ITEM_TYPE,
6333                 Data._ID, id_1_1,
6334                 GroupMembership.GROUP_ROW_ID, groupId2,
6335                 GroupMembership.GROUP_SOURCE_ID, "gsid2");
6336         assertDataRow(subValues[2], Email.CONTENT_ITEM_TYPE,
6337                 Data._ID, id_1_2,
6338                 Email.DATA, "c1@email.com");
6339         assertDataRow(subValues[3], Phone.CONTENT_ITEM_TYPE,
6340                 Data._ID, id_1_3,
6341                 Email.DATA, "5551212c1");
6342 
6343         entity = iterator.next();
6344         assertEquals(c2, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
6345         subValues = asSortedContentValuesArray(entity.getSubValues());
6346         assertEquals(3, subValues.length);
6347         assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
6348                 Data._ID, id_2_0,
6349                 GroupMembership.GROUP_ROW_ID, groupId1,
6350                 GroupMembership.GROUP_SOURCE_ID, "gsid1");
6351         assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
6352                 Data._ID, id_2_1,
6353                 Email.DATA, "c2@email.com");
6354         assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
6355                 Data._ID, id_2_2,
6356                 Email.DATA, "5551212c2");
6357 
6358         entity = iterator.next();
6359         assertEquals(c3, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
6360         subValues = asSortedContentValuesArray(entity.getSubValues());
6361         assertEquals(3, subValues.length);
6362         assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
6363                 Data._ID, id_3_0,
6364                 GroupMembership.GROUP_ROW_ID, groupId2,
6365                 GroupMembership.GROUP_SOURCE_ID, "gsid2");
6366         assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
6367                 Data._ID, id_3_1,
6368                 Email.DATA, "c3@email.com");
6369         assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
6370                 Data._ID, id_3_2,
6371                 Email.DATA, "5551212c3");
6372 
6373         assertFalse(iterator.hasNext());
6374         iterator.close();
6375     }
6376 
testDataCreateUpdateDeleteByMimeType()6377     public void testDataCreateUpdateDeleteByMimeType() throws Exception {
6378         long rawContactId = RawContactUtil.createRawContact(mResolver);
6379 
6380         ContentValues values = new ContentValues();
6381         values.put(Data.RAW_CONTACT_ID, rawContactId);
6382         values.put(Data.MIMETYPE, "testmimetype");
6383         values.put(Data.RES_PACKAGE, "oldpackage");
6384         values.put(Data.IS_PRIMARY, 1);
6385         values.put(Data.IS_SUPER_PRIMARY, 1);
6386         values.put(Data.DATA1, "old1");
6387         values.put(Data.DATA2, "old2");
6388         values.put(Data.DATA3, "old3");
6389         values.put(Data.DATA4, "old4");
6390         values.put(Data.DATA5, "old5");
6391         values.put(Data.DATA6, "old6");
6392         values.put(Data.DATA7, "old7");
6393         values.put(Data.DATA8, "old8");
6394         values.put(Data.DATA9, "old9");
6395         values.put(Data.DATA10, "old10");
6396         values.put(Data.DATA11, "old11");
6397         values.put(Data.DATA12, "old12");
6398         values.put(Data.DATA13, "old13");
6399         values.put(Data.DATA14, "old14");
6400         values.put(Data.DATA15, "old15");
6401         values.put(Data.CARRIER_PRESENCE, 0);
6402         values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, "oldcomponentname");
6403         values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "oldid");
6404         Uri uri = mResolver.insert(Data.CONTENT_URI, values);
6405         assertStoredValues(uri, values);
6406         assertNetworkNotified(true);
6407 
6408         values.clear();
6409         values.put(Data.RES_PACKAGE, "newpackage");
6410         values.put(Data.IS_PRIMARY, 0);
6411         values.put(Data.IS_SUPER_PRIMARY, 0);
6412         values.put(Data.DATA1, "new1");
6413         values.put(Data.DATA2, "new2");
6414         values.put(Data.DATA3, "new3");
6415         values.put(Data.DATA4, "new4");
6416         values.put(Data.DATA5, "new5");
6417         values.put(Data.DATA6, "new6");
6418         values.put(Data.DATA7, "new7");
6419         values.put(Data.DATA8, "new8");
6420         values.put(Data.DATA9, "new9");
6421         values.put(Data.DATA10, "new10");
6422         values.put(Data.DATA11, "new11");
6423         values.put(Data.DATA12, "new12");
6424         values.put(Data.DATA13, "new13");
6425         values.put(Data.DATA14, "new14");
6426         values.put(Data.DATA15, "new15");
6427         values.put(Data.CARRIER_PRESENCE, Data.CARRIER_PRESENCE_VT_CAPABLE);
6428         values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, "newcomponentname");
6429         values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "newid");
6430         mResolver.update(Data.CONTENT_URI, values, Data.RAW_CONTACT_ID + "=" + rawContactId +
6431                 " AND " + Data.MIMETYPE + "='testmimetype'", null);
6432         assertNetworkNotified(true);
6433 
6434         assertStoredValues(uri, values);
6435 
6436         int count = mResolver.delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
6437                 + " AND " + Data.MIMETYPE + "='testmimetype'", null);
6438         assertEquals(1, count);
6439         assertEquals(0, getCount(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
6440                 + " AND " + Data.MIMETYPE + "='testmimetype'", null));
6441         assertNetworkNotified(true);
6442     }
6443 
testRawContactQuery()6444     public void testRawContactQuery() {
6445         Account account1 = new Account("a", "b");
6446         Account account2 = new Account("c", "d");
6447         long rawContactId1 = RawContactUtil.createRawContact(mResolver, account1);
6448         long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2);
6449 
6450         Uri uri1 = TestUtil.maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account1);
6451         Uri uri2 = TestUtil.maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account2);
6452         assertEquals(1, getCount(uri1, null, null));
6453         assertEquals(1, getCount(uri2, null, null));
6454         assertStoredValue(uri1, RawContacts._ID, rawContactId1) ;
6455         assertStoredValue(uri2, RawContacts._ID, rawContactId2) ;
6456 
6457         Uri rowUri1 = ContentUris.withAppendedId(uri1, rawContactId1);
6458         Uri rowUri2 = ContentUris.withAppendedId(uri2, rawContactId2);
6459         assertStoredValue(rowUri1, RawContacts._ID, rawContactId1) ;
6460         assertStoredValue(rowUri2, RawContacts._ID, rawContactId2) ;
6461     }
6462 
testRawContactDeletion()6463     public void testRawContactDeletion() {
6464         long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6465         Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
6466 
6467         insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
6468         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
6469                 StatusUpdates.AVAILABLE, null,
6470                 StatusUpdates.CAPABILITY_HAS_CAMERA);
6471         long contactId = queryContactId(rawContactId);
6472 
6473         assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
6474                 null, null));
6475         assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
6476                 + rawContactId, null));
6477 
6478         mResolver.delete(uri, null, null);
6479 
6480         assertStoredValue(uri, RawContacts.DELETED, "1");
6481         assertNetworkNotified(true);
6482 
6483         Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount);
6484         mResolver.delete(permanentDeletionUri, null, null);
6485         assertEquals(0, getCount(uri, null, null));
6486         assertEquals(0, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
6487                 null, null));
6488         assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
6489                 + rawContactId, null));
6490         assertEquals(0, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null));
6491         assertNetworkNotified(false);
6492     }
6493 
testRawContactDeletionKeepingAggregateContact()6494     public void testRawContactDeletionKeepingAggregateContact() {
6495         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, mAccount);
6496         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, mAccount);
6497         setAggregationException(
6498                 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
6499 
6500         long contactId = queryContactId(rawContactId1);
6501 
6502         Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
6503         Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount);
6504         mResolver.delete(permanentDeletionUri, null, null);
6505         assertEquals(0, getCount(uri, null, null));
6506         assertEquals(1, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null));
6507     }
6508 
testRawContactDeletion_byAccountParam()6509     public void testRawContactDeletion_byAccountParam() {
6510         long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6511         Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
6512 
6513         insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
6514         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
6515                 StatusUpdates.AVAILABLE, null,
6516                 StatusUpdates.CAPABILITY_HAS_CAMERA);
6517         assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
6518                 null, null));
6519         assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
6520                 + rawContactId, null));
6521 
6522         // Do not delete if we are deleting with wrong account.
6523         Uri deleteWithWrongAccountUri =
6524             RawContacts.CONTENT_URI.buildUpon()
6525                 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccountTwo.name)
6526                 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccountTwo.type)
6527                 .build();
6528         int numDeleted = mResolver.delete(deleteWithWrongAccountUri, null, null);
6529         assertEquals(0, numDeleted);
6530 
6531         assertStoredValue(uri, RawContacts.DELETED, "0");
6532 
6533         // Delete if we are deleting with correct account.
6534         Uri deleteWithCorrectAccountUri =
6535             RawContacts.CONTENT_URI.buildUpon()
6536                 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccount.name)
6537                 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type)
6538                 .build();
6539         numDeleted = mResolver.delete(deleteWithCorrectAccountUri, null, null);
6540         assertEquals(1, numDeleted);
6541 
6542         assertStoredValue(uri, RawContacts.DELETED, "1");
6543     }
6544 
testRawContactDeletion_byAccountSelection()6545     public void testRawContactDeletion_byAccountSelection() {
6546         long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6547         Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
6548 
6549         // Do not delete if we are deleting with wrong account.
6550         int numDeleted = mResolver.delete(RawContacts.CONTENT_URI,
6551                 RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?",
6552                 new String[] {mAccountTwo.name, mAccountTwo.type});
6553         assertEquals(0, numDeleted);
6554 
6555         assertStoredValue(uri, RawContacts.DELETED, "0");
6556 
6557         // Delete if we are deleting with correct account.
6558         numDeleted = mResolver.delete(RawContacts.CONTENT_URI,
6559                 RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?",
6560                 new String[] {mAccount.name, mAccount.type});
6561         assertEquals(1, numDeleted);
6562 
6563         assertStoredValue(uri, RawContacts.DELETED, "1");
6564     }
6565 
6566     /**
6567      * Test for {@link ContactsProvider2#stringToAccounts} and
6568      * {@link ContactsProvider2#accountsToString}.
6569      */
testAccountsToString()6570     public void testAccountsToString() {
6571         final Set<Account> EXPECTED_0 = Sets.newHashSet();
6572         final Set<Account> EXPECTED_1 = Sets.newHashSet(TestUtil.ACCOUNT_1);
6573         final Set<Account> EXPECTED_2 = Sets.newHashSet(TestUtil.ACCOUNT_2);
6574         final Set<Account> EXPECTED_1_2 = Sets.newHashSet(TestUtil.ACCOUNT_1, TestUtil.ACCOUNT_2);
6575 
6576         final Set<Account> ACTUAL_0 = Sets.newHashSet();
6577         final Set<Account> ACTUAL_1 = Sets.newHashSet(TestUtil.ACCOUNT_1);
6578         final Set<Account> ACTUAL_2 = Sets.newHashSet(TestUtil.ACCOUNT_2);
6579         final Set<Account> ACTUAL_1_2 = Sets.newHashSet(TestUtil.ACCOUNT_2, TestUtil.ACCOUNT_1);
6580 
6581         assertTrue(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_0)));
6582         assertFalse(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_1)));
6583         assertFalse(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_2)));
6584         assertFalse(EXPECTED_0.equals(accountsToStringToAccounts(ACTUAL_1_2)));
6585 
6586         assertFalse(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_0)));
6587         assertTrue(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_1)));
6588         assertFalse(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_2)));
6589         assertFalse(EXPECTED_1.equals(accountsToStringToAccounts(ACTUAL_1_2)));
6590 
6591         assertFalse(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_0)));
6592         assertFalse(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_1)));
6593         assertTrue(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_2)));
6594         assertFalse(EXPECTED_2.equals(accountsToStringToAccounts(ACTUAL_1_2)));
6595 
6596         assertFalse(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_0)));
6597         assertFalse(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_1)));
6598         assertFalse(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_2)));
6599         assertTrue(EXPECTED_1_2.equals(accountsToStringToAccounts(ACTUAL_1_2)));
6600 
6601         try {
6602             ContactsProvider2.stringToAccounts("x");
6603             fail("Didn't throw for malformed input");
6604         } catch (IllegalArgumentException expected) {
6605         }
6606     }
6607 
accountsToStringToAccounts(Set<Account> accounts)6608     private static final Set<Account> accountsToStringToAccounts(Set<Account> accounts) {
6609         return ContactsProvider2.stringToAccounts(ContactsProvider2.accountsToString(accounts));
6610     }
6611 
6612     /**
6613      * Test for {@link ContactsProvider2#haveAccountsChanged} and
6614      * {@link ContactsProvider2#saveAccounts}.
6615      */
testHaveAccountsChanged()6616     public void testHaveAccountsChanged() {
6617         final ContactsProvider2 cp = (ContactsProvider2) getProvider();
6618 
6619         final Account[] ACCOUNTS_0 = new Account[] {};
6620         final Account[] ACCOUNTS_1 = new Account[] {TestUtil.ACCOUNT_1};
6621         final Account[] ACCOUNTS_2 = new Account[] {TestUtil.ACCOUNT_2};
6622         final Account[] ACCOUNTS_1_2 = new Account[] {TestUtil.ACCOUNT_1, TestUtil.ACCOUNT_2};
6623         final Account[] ACCOUNTS_2_1 = new Account[] {TestUtil.ACCOUNT_2, TestUtil.ACCOUNT_1};
6624 
6625         // Add ACCOUNT_1
6626 
6627         assertTrue(cp.haveAccountsChanged(ACCOUNTS_1));
6628         cp.saveAccounts(ACCOUNTS_1);
6629         assertFalse(cp.haveAccountsChanged(ACCOUNTS_1));
6630 
6631         // Add ACCOUNT_2
6632 
6633         assertTrue(cp.haveAccountsChanged(ACCOUNTS_1_2));
6634         // (try with reverse order)
6635         assertTrue(cp.haveAccountsChanged(ACCOUNTS_2_1));
6636         cp.saveAccounts(ACCOUNTS_1_2);
6637         assertFalse(cp.haveAccountsChanged(ACCOUNTS_1_2));
6638         // (try with reverse order)
6639         assertFalse(cp.haveAccountsChanged(ACCOUNTS_2_1));
6640 
6641         // Remove ACCOUNT_1
6642 
6643         assertTrue(cp.haveAccountsChanged(ACCOUNTS_2));
6644         cp.saveAccounts(ACCOUNTS_2);
6645         assertFalse(cp.haveAccountsChanged(ACCOUNTS_2));
6646 
6647         // Remove ACCOUNT_2
6648 
6649         assertTrue(cp.haveAccountsChanged(ACCOUNTS_0));
6650         cp.saveAccounts(ACCOUNTS_0);
6651         assertFalse(cp.haveAccountsChanged(ACCOUNTS_0));
6652 
6653         // Test with malformed DB property.
6654 
6655         final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
6656         dbHelper.setProperty(DbProperties.KNOWN_ACCOUNTS, "x");
6657 
6658         // With malformed property the method always return true.
6659         assertTrue(cp.haveAccountsChanged(ACCOUNTS_0));
6660         assertTrue(cp.haveAccountsChanged(ACCOUNTS_1));
6661     }
6662 
testAccountsUpdated()6663     public void testAccountsUpdated() {
6664         // This is to ensure we do not delete contacts with null, null (account name, type)
6665         // accidentally.
6666         long rawContactId3 = RawContactUtil.createRawContactWithName(mResolver, "James", "Sullivan");
6667         insertPhoneNumber(rawContactId3, "5234567890");
6668         Uri rawContact3 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId3);
6669         assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null));
6670 
6671         ContactsProvider2 cp = (ContactsProvider2) getProvider();
6672         mActor.setAccounts(new Account[]{mAccount, mAccountTwo});
6673         cp.onAccountsUpdated(new Account[]{mAccount, mAccountTwo});
6674         assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null));
6675         assertStoredValue(
6676                 rawContact3, RawContacts.ACCOUNT_NAME,
6677                 AccountWithDataSet.LOCAL.getAccountName());
6678         assertStoredValue(rawContact3, RawContacts.ACCOUNT_TYPE,
6679                 AccountWithDataSet.LOCAL.getAccountType());
6680 
6681         long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount);
6682         insertEmail(rawContactId1, "account1@email.com");
6683         long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccountTwo);
6684         insertEmail(rawContactId2, "account2@email.com");
6685         insertImHandle(rawContactId2, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com");
6686         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme@android.com",
6687                 StatusUpdates.AVAILABLE, null,
6688                 StatusUpdates.CAPABILITY_HAS_CAMERA);
6689 
6690         mActor.setAccounts(new Account[]{mAccount});
6691         cp.onAccountsUpdated(new Account[]{mAccount});
6692         assertEquals(2, getCount(RawContacts.CONTENT_URI, null, null));
6693         assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
6694                 + rawContactId2, null));
6695     }
6696 
testAccountDeletion()6697     public void testAccountDeletion() {
6698         Account readOnlyAccount = new Account("act", READ_ONLY_ACCOUNT_TYPE);
6699         ContactsProvider2 cp = (ContactsProvider2) getProvider();
6700         mActor.setAccounts(new Account[]{readOnlyAccount, mAccount});
6701         cp.onAccountsUpdated(new Account[]{readOnlyAccount, mAccount});
6702 
6703         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe",
6704                 readOnlyAccount);
6705         Uri photoUri1 = insertPhoto(rawContactId1);
6706         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "john", "doe",
6707                 mAccount);
6708         Uri photoUri2 = insertPhoto(rawContactId2);
6709         storeValue(photoUri2, Photo.IS_SUPER_PRIMARY, "1");
6710 
6711         assertAggregated(rawContactId1, rawContactId2);
6712 
6713         long contactId = queryContactId(rawContactId1);
6714 
6715         // The display name should come from the writable account
6716         assertStoredValue(Uri.withAppendedPath(
6717                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
6718                 Contacts.Data.CONTENT_DIRECTORY),
6719                 Contacts.DISPLAY_NAME, "john doe");
6720 
6721         // The photo should be the one we marked as super-primary
6722         assertStoredValue(Contacts.CONTENT_URI, contactId,
6723                 Contacts.PHOTO_ID, ContentUris.parseId(photoUri2));
6724 
6725         mActor.setAccounts(new Account[]{readOnlyAccount});
6726         // Remove the writable account
6727         cp.onAccountsUpdated(new Account[]{readOnlyAccount});
6728 
6729         // The display name should come from the remaining account
6730         assertStoredValue(Uri.withAppendedPath(
6731                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
6732                 Contacts.Data.CONTENT_DIRECTORY),
6733                 Contacts.DISPLAY_NAME, "John Doe");
6734 
6735         // The photo should be the remaining one
6736         assertStoredValue(Contacts.CONTENT_URI, contactId,
6737                 Contacts.PHOTO_ID, ContentUris.parseId(photoUri1));
6738     }
6739 
testStreamItemsCleanedUpOnAccountRemoval()6740     public void testStreamItemsCleanedUpOnAccountRemoval() {
6741         Account doomedAccount = new Account("doom", "doom");
6742         Account safeAccount = mAccount;
6743         ContactsProvider2 cp = (ContactsProvider2) getProvider();
6744         mActor.setAccounts(new Account[]{doomedAccount, safeAccount});
6745         cp.onAccountsUpdated(new Account[]{doomedAccount, safeAccount});
6746 
6747         // Create a doomed raw contact, stream item, and photo.
6748         long doomedRawContactId = RawContactUtil.createRawContactWithName(mResolver, doomedAccount);
6749         Uri doomedStreamItemUri =
6750                 insertStreamItem(doomedRawContactId, buildGenericStreamItemValues(), doomedAccount);
6751         long doomedStreamItemId = ContentUris.parseId(doomedStreamItemUri);
6752         Uri doomedStreamItemPhotoUri = insertStreamItemPhoto(
6753                 doomedStreamItemId, buildGenericStreamItemPhotoValues(0), doomedAccount);
6754 
6755         // Create a safe raw contact, stream item, and photo.
6756         long safeRawContactId = RawContactUtil.createRawContactWithName(mResolver, safeAccount);
6757         Uri safeStreamItemUri =
6758                 insertStreamItem(safeRawContactId, buildGenericStreamItemValues(), safeAccount);
6759         long safeStreamItemId = ContentUris.parseId(safeStreamItemUri);
6760         Uri safeStreamItemPhotoUri = insertStreamItemPhoto(
6761                 safeStreamItemId, buildGenericStreamItemPhotoValues(0), safeAccount);
6762         long safeStreamItemPhotoId = ContentUris.parseId(safeStreamItemPhotoUri);
6763 
6764         // Remove the doomed account.
6765         mActor.setAccounts(new Account[]{safeAccount});
6766         cp.onAccountsUpdated(new Account[]{safeAccount});
6767 
6768         // Check that the doomed stuff has all been nuked.
6769         ContentValues[] noValues = new ContentValues[0];
6770         assertStoredValues(ContentUris.withAppendedId(RawContacts.CONTENT_URI, doomedRawContactId),
6771                 noValues);
6772         assertStoredValues(doomedStreamItemUri, noValues);
6773         assertStoredValues(doomedStreamItemPhotoUri, noValues);
6774 
6775         // Check that the safe stuff lives on.
6776         assertStoredValue(RawContacts.CONTENT_URI, safeRawContactId, RawContacts._ID,
6777                 safeRawContactId);
6778         assertStoredValue(safeStreamItemUri, StreamItems._ID, safeStreamItemId);
6779         assertStoredValue(safeStreamItemPhotoUri, StreamItemPhotos._ID, safeStreamItemPhotoId);
6780     }
6781 
testContactDeletion()6782     public void testContactDeletion() {
6783         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe",
6784                 TestUtil.ACCOUNT_1);
6785         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe",
6786                 TestUtil.ACCOUNT_2);
6787 
6788         long contactId = queryContactId(rawContactId1);
6789 
6790         mResolver.delete(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), null, null);
6791 
6792         assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
6793                 RawContacts.DELETED, "1");
6794         assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2),
6795                 RawContacts.DELETED, "1");
6796     }
6797 
testMarkAsDirtyParameter()6798     public void testMarkAsDirtyParameter() {
6799         long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6800         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
6801 
6802         Uri uri = DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe");
6803         clearDirty(rawContactUri);
6804         Uri updateUri = setCallerIsSyncAdapter(uri, mAccount);
6805 
6806         ContentValues values = new ContentValues();
6807         values.put(StructuredName.FAMILY_NAME, "Dough");
6808         mResolver.update(updateUri, values, null, null);
6809         assertStoredValue(uri, StructuredName.FAMILY_NAME, "Dough");
6810         assertDirty(rawContactUri, false);
6811         assertNetworkNotified(false);
6812     }
6813 
testDirtyWhenRawContactInsert()6814     public void testDirtyWhenRawContactInsert() {
6815         // When inserting a rawcontact.
6816         long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6817         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
6818         assertDirty(rawContactUri, false);
6819         assertMetadataDirty(rawContactUri, false);
6820         assertNetworkNotified(true);
6821     }
6822 
testRawContactDirtyAndVersion()6823     public void testRawContactDirtyAndVersion() {
6824         final long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6825         Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId);
6826         assertDirty(uri, false);
6827         long version = getVersion(uri);
6828 
6829         ContentValues values = new ContentValues();
6830         values.put(ContactsContract.RawContacts.SEND_TO_VOICEMAIL, 1);
6831         values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
6832                 RawContacts.AGGREGATION_MODE_IMMEDIATE);
6833         assertEquals(1, mResolver.update(uri, values, null, null));
6834         assertEquals(version, getVersion(uri));
6835 
6836         assertDirty(uri, false);
6837         assertMetadataDirty(uri, false);
6838         assertNetworkNotified(false);
6839 
6840         Uri emailUri = insertEmail(rawContactId, "goo@woo.com");
6841         assertDirty(uri, true);
6842         assertNetworkNotified(true);
6843         ++version;
6844         assertEquals(version, getVersion(uri));
6845         clearDirty(uri);
6846 
6847         values = new ContentValues();
6848         values.put(Email.DATA, "goo@hoo.com");
6849         mResolver.update(emailUri, values, null, null);
6850         assertDirty(uri, true);
6851         assertNetworkNotified(true);
6852         ++version;
6853         assertEquals(version, getVersion(uri));
6854         clearDirty(uri);
6855 
6856         mResolver.delete(emailUri, null, null);
6857         assertDirty(uri, true);
6858         assertNetworkNotified(true);
6859         ++version;
6860         assertEquals(version, getVersion(uri));
6861     }
6862 
testRawContactClearDirty()6863     public void testRawContactClearDirty() {
6864         final long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6865         Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
6866                 rawContactId);
6867         long version = getVersion(uri);
6868         insertEmail(rawContactId, "goo@woo.com");
6869         assertDirty(uri, true);
6870         version++;
6871         assertEquals(version, getVersion(uri));
6872 
6873         clearDirty(uri);
6874         assertDirty(uri, false);
6875         assertEquals(version, getVersion(uri));
6876     }
6877 
testRawContactDeletionSetsDirty()6878     public void testRawContactDeletionSetsDirty() {
6879         final long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6880         Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
6881                 rawContactId);
6882         long version = getVersion(uri);
6883         clearDirty(uri);
6884         assertDirty(uri, false);
6885 
6886         mResolver.delete(uri, null, null);
6887         assertStoredValue(uri, RawContacts.DELETED, "1");
6888         assertDirty(uri, true);
6889         assertNetworkNotified(true);
6890         version++;
6891         assertEquals(version, getVersion(uri));
6892     }
6893 
testNotifyMetadataChangeForRawContactInsertBySyncAdapter()6894     public void testNotifyMetadataChangeForRawContactInsertBySyncAdapter() {
6895         Uri uri = RawContacts.CONTENT_URI.buildUpon()
6896                 .appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccount.name)
6897                 .appendQueryParameter(RawContacts.ACCOUNT_TYPE, mAccount.type)
6898                 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, true + "")
6899                 .build();
6900 
6901         long rawContactId = ContentUris.parseId(mResolver.insert(uri, new ContentValues()));
6902         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
6903         assertMetadataDirty(rawContactUri, false);
6904     }
6905 
testMarkAsMetadataDirtyForRawContactMetadataChange()6906     public void testMarkAsMetadataDirtyForRawContactMetadataChange() {
6907         long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6908         long contactId = queryContactId(rawContactId);
6909         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
6910 
6911         ContentValues values = new ContentValues();
6912         values.put(Contacts.STARRED, 1);
6913         mResolver.update(contactUri, values, null, null);
6914         assertStoredValue(contactUri, Contacts.STARRED, 1);
6915 
6916         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
6917         assertMetadataDirty(rawContactUri, false);
6918 
6919         clearMetadataDirty(rawContactUri);
6920         values = new ContentValues();
6921         values.put(Contacts.PINNED, 1);
6922         mResolver.update(contactUri, values, null, null);
6923         assertStoredValue(contactUri, Contacts.PINNED, 1);
6924 
6925         assertMetadataDirty(rawContactUri, false);
6926 
6927         clearMetadataDirty(rawContactUri);
6928         values = new ContentValues();
6929         values.put(Contacts.SEND_TO_VOICEMAIL, 1);
6930         mResolver.update(contactUri, values, null, null);
6931         assertStoredValue(contactUri, Contacts.SEND_TO_VOICEMAIL, 1);
6932 
6933         assertMetadataDirty(rawContactUri, false);
6934     }
6935 
testMarkAsMetadataDirtyForRawContactBackupIdChange()6936     public void testMarkAsMetadataDirtyForRawContactBackupIdChange() {
6937         long rawContactId = RawContactUtil.createRawContact(mResolver, mAccount);
6938         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
6939 
6940         // Make a metadata change to set metadata_dirty.
6941         ContentValues values = new ContentValues();
6942         values.put(RawContacts.SEND_TO_VOICEMAIL, "1");
6943         mResolver.update(rawContactUri, values, null, null);
6944         assertMetadataDirty(rawContactUri, false);
6945 
6946         // Update the backup_id and check metadata network should be notified.
6947         values = new ContentValues();
6948         values.put(RawContacts.BACKUP_ID, "newBackupId");
6949         mResolver.update(rawContactUri, values, null, null);
6950         assertStoredValue(rawContactUri, RawContacts.BACKUP_ID, "newBackupId");
6951         assertMetadataDirty(rawContactUri, false);
6952     }
6953 
testMarkAsMetadataDirtyForAggregationExceptionChange()6954     public void testMarkAsMetadataDirtyForAggregationExceptionChange() {
6955         long rawContactId1 = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
6956         long rawContactId2 = RawContactUtil.createRawContact(mResolver, new Account("b", "b"));
6957 
6958         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
6959                 rawContactId1, rawContactId2);
6960 
6961         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
6962                 false);
6963         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2),
6964                 false);
6965     }
6966 
testMarkAsMetadataNotDirtyForUsageStatsChange()6967     public void testMarkAsMetadataNotDirtyForUsageStatsChange() {
6968         final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "contact", "a");
6969         final long did1a = ContentUris.parseId(insertEmail(rid1, "email_1_a@email.com"));
6970         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1a);
6971 
6972         // Usage feedback no longer works, so "false".
6973         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rid1), false);
6974     }
6975 
testMarkAsMetadataDirtyForDataPrimarySettingInsert()6976     public void testMarkAsMetadataDirtyForDataPrimarySettingInsert() {
6977         long rawContactId1 = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
6978         Uri mailUri11 = insertEmail(rawContactId1, "test1@domain1.com", true, true);
6979 
6980         assertStoredValue(mailUri11, Data.IS_PRIMARY, 1);
6981         assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 1);
6982         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
6983                 false);
6984     }
6985 
testMarkAsMetadataDirtyForDataPrimarySettingUpdate()6986     public void testMarkAsMetadataDirtyForDataPrimarySettingUpdate() {
6987         long rawContactId = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
6988         Uri mailUri1 = insertEmail(rawContactId, "test1@domain1.com");
6989 
6990         assertStoredValue(mailUri1, Data.IS_PRIMARY, 0);
6991         assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, 0);
6992 
6993         ContentValues values = new ContentValues();
6994         values.put(Data.IS_SUPER_PRIMARY, 1);
6995         mResolver.update(mailUri1, values, null, null);
6996 
6997         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
6998                 false);
6999     }
7000 
testMarkAsMetadataDirtyForDataDelete()7001     public void testMarkAsMetadataDirtyForDataDelete() {
7002         long rawContactId = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
7003         Uri mailUri1 = insertEmail(rawContactId, "test1@domain1.com", true, true);
7004 
7005         mResolver.delete(mailUri1, null, null);
7006 
7007         assertMetadataDirty(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
7008                 false);
7009     }
7010 
testDeleteContactWithoutName()7011     public void testDeleteContactWithoutName() {
7012         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
7013         long rawContactId = ContentUris.parseId(rawContactUri);
7014 
7015         Uri phoneUri = insertPhoneNumber(rawContactId, "555-123-45678", true);
7016 
7017         long contactId = queryContactId(rawContactId);
7018         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7019         Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
7020 
7021         int numDeleted = mResolver.delete(lookupUri, null, null);
7022         assertEquals(1, numDeleted);
7023     }
7024 
testDeleteContactWithoutAnyData()7025     public void testDeleteContactWithoutAnyData() {
7026         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
7027         long rawContactId = ContentUris.parseId(rawContactUri);
7028 
7029         long contactId = queryContactId(rawContactId);
7030         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7031         Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
7032 
7033         int numDeleted = mResolver.delete(lookupUri, null, null);
7034         assertEquals(1, numDeleted);
7035     }
7036 
testDeleteContactWithEscapedUri()7037     public void testDeleteContactWithEscapedUri() {
7038         ContentValues values = new ContentValues();
7039         values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
7040         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
7041         long rawContactId = ContentUris.parseId(rawContactUri);
7042 
7043         long contactId = queryContactId(rawContactId);
7044         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7045         Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
7046         assertEquals(1, mResolver.delete(lookupUri, null, null));
7047     }
7048 
testDeleteContactComposedOfSingleLocalRawContact()7049     public void testDeleteContactComposedOfSingleLocalRawContact() {
7050         // Create a raw contact in the local (null) account
7051         long rawContactId = RawContactUtil.createRawContact(mResolver, null);
7052         DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Smith");
7053 
7054         // Delete the contact
7055         long contactId = queryContactId(rawContactId);
7056         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7057         assertEquals(1, mResolver.delete(contactUri, null, null));
7058 
7059         // Assert that the raw contact was removed
7060         Cursor c1 = queryRawContact(rawContactId);
7061         assertEquals(0, c1.getCount());
7062         c1.close();
7063 
7064         // Assert that the contact was removed
7065         Cursor c2 = mResolver.query(contactUri, null, null, null, "");
7066         assertEquals(0, c2.getCount());
7067         c2.close();
7068     }
7069 
testDeleteContactComposedOfTwoLocalRawContacts()7070     public void testDeleteContactComposedOfTwoLocalRawContacts() {
7071         // Create a raw contact in the local (null) account
7072         long rawContactId1 = RawContactUtil.createRawContact(mResolver, null);
7073         DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Smith");
7074 
7075         // Create another local raw contact with the same name
7076         long rawContactId2 = RawContactUtil.createRawContact(mResolver, null);
7077         DataUtil.insertStructuredName(mResolver, rawContactId2, "John", "Smith");
7078 
7079         // Join the two raw contacts explicitly
7080         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
7081                 rawContactId1, rawContactId2);
7082 
7083         // Check that the two raw contacts are aggregated together
7084         assertAggregated(rawContactId1, rawContactId2, "John Smith");
7085 
7086         // Delete the aggregate contact
7087         long contactId = queryContactId(rawContactId1);
7088         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7089         assertEquals(1, mResolver.delete(contactUri, null, null));
7090 
7091         // Assert that both of the local raw contacts were removed completely
7092         Cursor c1 = queryRawContact(rawContactId1);
7093         assertEquals(0, c1.getCount());
7094         c1.close();
7095 
7096         Cursor c2 = queryRawContact(rawContactId2);
7097         assertEquals(0, c2.getCount());
7098         c2.close();
7099 
7100         // Assert that the contact was removed
7101         Cursor c3 = queryContact(contactId);
7102         assertEquals(0, c3.getCount());
7103         c3.close();
7104     }
7105 
testDeleteContactComposedOfSomeLocalRawContacts()7106     public void testDeleteContactComposedOfSomeLocalRawContacts() {
7107         // Create a raw contact in the local (null) account
7108         long rawContactId1 = RawContactUtil.createRawContact(mResolver, null);
7109         DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Smith");
7110 
7111         // Create another one in a non-local account with the same name
7112         long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccount);
7113         DataUtil.insertStructuredName(mResolver, rawContactId2, "John", "Smith");
7114 
7115         // Check that the two new raw contacts are aggregated together
7116         assertAggregated(rawContactId1, rawContactId2, "John Smith");
7117 
7118         // Delete the aggregate contact
7119         long contactId = queryContactId(rawContactId1);
7120         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7121         assertEquals(1, mResolver.delete(contactUri, null, null));
7122 
7123         // Assert that the local raw contact was removed completely
7124         Cursor c1 = queryRawContact(rawContactId1);
7125         assertEquals(0, c1.getCount());
7126         c1.close();
7127 
7128         // Assert that the non-local raw contact is still present just marked as deleted
7129         Cursor c2 = queryRawContact(rawContactId2);
7130         assertEquals(1, c2.getCount());
7131         assertTrue(c2.moveToFirst());
7132         assertEquals(1, c2.getInt(c2.getColumnIndex(RawContacts.DELETED)));
7133         c2.close();
7134 
7135         // Assert that the contact was removed
7136         Cursor c3 = queryContact(contactId);
7137         assertEquals(0, c3.getCount());
7138         c3.close();
7139     }
7140 
testQueryContactWithEscapedUri()7141     public void testQueryContactWithEscapedUri() {
7142         ContentValues values = new ContentValues();
7143         values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
7144         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
7145         long rawContactId = ContentUris.parseId(rawContactUri);
7146 
7147         long contactId = queryContactId(rawContactId);
7148         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7149         Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
7150         Cursor c = mResolver.query(lookupUri, null, null, null, "");
7151         assertEquals(1, c.getCount());
7152         c.close();
7153     }
7154 
testGetPhotoUri()7155     public void testGetPhotoUri() {
7156         ContentValues values = new ContentValues();
7157         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
7158         long rawContactId = ContentUris.parseId(rawContactUri);
7159         DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe");
7160         long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
7161         long photoFileId = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?",
7162                 new String[]{String.valueOf(dataId)}, Photo.PHOTO_FILE_ID);
7163         String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId)
7164                 .toString();
7165 
7166         assertStoredValue(
7167                 ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId)),
7168                 Contacts.PHOTO_URI, photoUri);
7169     }
7170 
testGetPhotoViaLookupUri()7171     public void testGetPhotoViaLookupUri() throws IOException {
7172         long rawContactId = RawContactUtil.createRawContact(mResolver);
7173         long contactId = queryContactId(rawContactId);
7174         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7175         Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
7176         String lookupKey = lookupUri.getPathSegments().get(2);
7177         insertPhoto(rawContactId, R.drawable.earth_small);
7178         byte[] thumbnail = loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL);
7179 
7180         // Two forms of lookup key URIs should be valid - one with the contact ID, one without.
7181         Uri photoLookupUriWithId = Uri.withAppendedPath(lookupUri, "photo");
7182         Uri photoLookupUriWithoutId = Contacts.CONTENT_LOOKUP_URI.buildUpon()
7183                 .appendPath(lookupKey).appendPath("photo").build();
7184 
7185         // Try retrieving as a data record.
7186         ContentValues values = new ContentValues();
7187         values.put(Photo.PHOTO, thumbnail);
7188         assertStoredValues(photoLookupUriWithId, values);
7189         assertStoredValues(photoLookupUriWithoutId, values);
7190 
7191         // Try opening as an input stream.
7192         EvenMoreAsserts.assertImageRawData(getContext(),
7193                 thumbnail, mResolver.openInputStream(photoLookupUriWithId));
7194         EvenMoreAsserts.assertImageRawData(getContext(),
7195                 thumbnail, mResolver.openInputStream(photoLookupUriWithoutId));
7196     }
7197 
testInputStreamForPhoto()7198     public void testInputStreamForPhoto() throws Exception {
7199         long rawContactId = RawContactUtil.createRawContact(mResolver);
7200         long contactId = queryContactId(rawContactId);
7201         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7202         insertPhoto(rawContactId);
7203         Uri photoUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_URI));
7204         Uri photoThumbnailUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI));
7205 
7206         // Check the thumbnail.
7207         EvenMoreAsserts.assertImageRawData(getContext(), loadTestPhoto(PhotoSize.THUMBNAIL),
7208                 mResolver.openInputStream(photoThumbnailUri));
7209 
7210         // Then check the display photo.  Note because we only inserted a small photo, but not a
7211         // display photo, this returns the thumbnail image itself, which was compressed at
7212         // the thumnail compression rate, which is why we compare to
7213         // loadTestPhoto(PhotoSize.THUMBNAIL) rather than loadTestPhoto(PhotoSize.DISPLAY_PHOTO)
7214         // here.
7215         // (In other words, loadTestPhoto(PhotoSize.DISPLAY_PHOTO) returns the same photo as
7216         // loadTestPhoto(PhotoSize.THUMBNAIL), except it's compressed at a lower compression rate.)
7217         EvenMoreAsserts.assertImageRawData(getContext(), loadTestPhoto(PhotoSize.THUMBNAIL),
7218                 mResolver.openInputStream(photoUri));
7219     }
7220 
testSuperPrimaryPhoto()7221     public void testSuperPrimaryPhoto() {
7222         long rawContactId1 = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
7223         Uri photoUri1 = insertPhoto(rawContactId1, R.drawable.earth_normal);
7224         long photoId1 = ContentUris.parseId(photoUri1);
7225 
7226         long rawContactId2 = RawContactUtil.createRawContact(mResolver, new Account("b", "b"));
7227         Uri photoUri2 = insertPhoto(rawContactId2, R.drawable.earth_normal);
7228         long photoId2 = ContentUris.parseId(photoUri2);
7229 
7230         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
7231                 rawContactId1, rawContactId2);
7232 
7233         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
7234                 queryContactId(rawContactId1));
7235 
7236         long photoFileId1 = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?",
7237                 new String[]{String.valueOf(photoId1)}, Photo.PHOTO_FILE_ID);
7238         String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId1)
7239                 .toString();
7240         assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
7241         assertStoredValue(contactUri, Contacts.PHOTO_URI, photoUri);
7242 
7243         setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
7244                 rawContactId1, rawContactId2);
7245 
7246         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
7247                 rawContactId1, rawContactId2);
7248         ContentValues values = new ContentValues();
7249         values.put(Data.IS_SUPER_PRIMARY, 1);
7250         mResolver.update(photoUri2, values, null, null);
7251 
7252         contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
7253                 queryContactId(rawContactId1));
7254         assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId2);
7255 
7256         mResolver.update(photoUri1, values, null, null);
7257         assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
7258     }
7259 
testUpdatePhoto()7260     public void testUpdatePhoto() {
7261         ContentValues values = new ContentValues();
7262         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
7263         long rawContactId = ContentUris.parseId(rawContactUri);
7264         DataUtil.insertStructuredName(mResolver, rawContactId, "John", "Doe");
7265 
7266         Uri twigUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
7267                 queryContactId(rawContactId)), Contacts.Photo.CONTENT_DIRECTORY);
7268 
7269         values.clear();
7270         values.put(Data.RAW_CONTACT_ID, rawContactId);
7271         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7272         values.putNull(Photo.PHOTO);
7273         Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
7274         long photoId = ContentUris.parseId(dataUri);
7275 
7276         assertEquals(0, getCount(twigUri, null, null));
7277 
7278         values.clear();
7279         values.put(Photo.PHOTO, loadTestPhoto());
7280         mResolver.update(dataUri, values, null, null);
7281         assertNetworkNotified(true);
7282 
7283         long twigId = getStoredLongValue(twigUri, Data._ID);
7284         assertEquals(photoId, twigId);
7285     }
7286 
testUpdateRawContactDataPhoto()7287     public void testUpdateRawContactDataPhoto() {
7288         // setup a contact with a null photo
7289         ContentValues values = new ContentValues();
7290         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
7291         long rawContactId = ContentUris.parseId(rawContactUri);
7292 
7293         // setup a photo
7294         values.put(Data.RAW_CONTACT_ID, rawContactId);
7295         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7296         values.putNull(Photo.PHOTO);
7297 
7298         // try to do an update before insert should return count == 0
7299         Uri dataUri = Uri.withAppendedPath(
7300                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
7301                 RawContacts.Data.CONTENT_DIRECTORY);
7302         assertEquals(0, mResolver.update(dataUri, values, Data.MIMETYPE + "=?",
7303                 new String[] {Photo.CONTENT_ITEM_TYPE}));
7304 
7305         mResolver.insert(Data.CONTENT_URI, values);
7306 
7307         // save a photo to the db
7308         values.clear();
7309         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7310         values.put(Photo.PHOTO, loadTestPhoto());
7311         assertEquals(1, mResolver.update(dataUri, values, Data.MIMETYPE + "=?",
7312                 new String[] {Photo.CONTENT_ITEM_TYPE}));
7313 
7314         // verify the photo
7315         Cursor storedPhoto = mResolver.query(dataUri, new String[] {Photo.PHOTO},
7316                 Data.MIMETYPE + "=?", new String[] {Photo.CONTENT_ITEM_TYPE}, null);
7317         storedPhoto.moveToFirst();
7318         MoreAsserts.assertEquals(loadTestPhoto(PhotoSize.THUMBNAIL), storedPhoto.getBlob(0));
7319         storedPhoto.close();
7320     }
7321 
testOpenDisplayPhotoForContactId()7322     public void testOpenDisplayPhotoForContactId() throws IOException {
7323         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
7324         long contactId = queryContactId(rawContactId);
7325         insertPhoto(rawContactId, R.drawable.earth_normal);
7326         Uri photoUri = Contacts.CONTENT_URI.buildUpon()
7327                 .appendPath(String.valueOf(contactId))
7328                 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
7329         EvenMoreAsserts.assertImageRawData(getContext(),
7330                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
7331                 mResolver.openInputStream(photoUri));
7332     }
7333 
testOpenDisplayPhotoForContactLookupKey()7334     public void testOpenDisplayPhotoForContactLookupKey() throws IOException {
7335         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
7336         long contactId = queryContactId(rawContactId);
7337         String lookupKey = queryLookupKey(contactId);
7338         insertPhoto(rawContactId, R.drawable.earth_normal);
7339         Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon()
7340                 .appendPath(lookupKey)
7341                 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
7342         EvenMoreAsserts.assertImageRawData(getContext(),
7343                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
7344                 mResolver.openInputStream(photoUri));
7345     }
7346 
testOpenDisplayPhotoForContactLookupKeyAndId()7347     public void testOpenDisplayPhotoForContactLookupKeyAndId() throws IOException {
7348         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
7349         long contactId = queryContactId(rawContactId);
7350         String lookupKey = queryLookupKey(contactId);
7351         insertPhoto(rawContactId, R.drawable.earth_normal);
7352         Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon()
7353                 .appendPath(lookupKey)
7354                 .appendPath(String.valueOf(contactId))
7355                 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
7356         EvenMoreAsserts.assertImageRawData(getContext(),
7357                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
7358                 mResolver.openInputStream(photoUri));
7359     }
7360 
testOpenDisplayPhotoForRawContactId()7361     public void testOpenDisplayPhotoForRawContactId() throws IOException {
7362         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
7363         insertPhoto(rawContactId, R.drawable.earth_normal);
7364         Uri photoUri = RawContacts.CONTENT_URI.buildUpon()
7365                 .appendPath(String.valueOf(rawContactId))
7366                 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
7367         EvenMoreAsserts.assertImageRawData(getContext(),
7368                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
7369                 mResolver.openInputStream(photoUri));
7370     }
7371 
testOpenDisplayPhotoByPhotoUri()7372     public void testOpenDisplayPhotoByPhotoUri() throws IOException {
7373         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
7374         long contactId = queryContactId(rawContactId);
7375         insertPhoto(rawContactId, R.drawable.earth_normal);
7376 
7377         // Get the photo URI out and check the content.
7378         String photoUri = getStoredValue(
7379                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
7380                 Contacts.PHOTO_URI);
7381         EvenMoreAsserts.assertImageRawData(getContext(),
7382                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
7383                 mResolver.openInputStream(Uri.parse(photoUri)));
7384     }
7385 
testPhotoUriForDisplayPhoto()7386     public void testPhotoUriForDisplayPhoto() {
7387         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
7388         long contactId = queryContactId(rawContactId);
7389 
7390         // Photo being inserted is larger than a thumbnail, so it will be stored as a file.
7391         long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
7392         String photoFileId = getStoredValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
7393                 Photo.PHOTO_FILE_ID);
7394         String photoUri = getStoredValue(
7395                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
7396                 Contacts.PHOTO_URI);
7397 
7398         // Check that the photo URI differs from the thumbnail.
7399         String thumbnailUri = getStoredValue(
7400                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
7401                 Contacts.PHOTO_THUMBNAIL_URI);
7402         assertFalse(photoUri.equals(thumbnailUri));
7403 
7404         // URI should be of the form display_photo/ID
7405         assertEquals(Uri.withAppendedPath(DisplayPhoto.CONTENT_URI, photoFileId).toString(),
7406                 photoUri);
7407     }
7408 
testPhotoUriForThumbnailPhoto()7409     public void testPhotoUriForThumbnailPhoto() throws IOException {
7410         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
7411         long contactId = queryContactId(rawContactId);
7412 
7413         // Photo being inserted is a thumbnail, so it will only be stored in a BLOB.  The photo URI
7414         // will fall back to the thumbnail URI.
7415         insertPhoto(rawContactId, R.drawable.earth_small);
7416         String photoUri = getStoredValue(
7417                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
7418                 Contacts.PHOTO_URI);
7419 
7420         // Check that the photo URI is equal to the thumbnail URI.
7421         String thumbnailUri = getStoredValue(
7422                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
7423                 Contacts.PHOTO_THUMBNAIL_URI);
7424         assertEquals(photoUri, thumbnailUri);
7425 
7426         // URI should be of the form contacts/ID/photo
7427         assertEquals(Uri.withAppendedPath(
7428                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
7429                 Contacts.Photo.CONTENT_DIRECTORY).toString(),
7430                 photoUri);
7431 
7432         // Loading the photo URI content should get the thumbnail.
7433         EvenMoreAsserts.assertImageRawData(getContext(),
7434                 loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL),
7435                 mResolver.openInputStream(Uri.parse(photoUri)));
7436     }
7437 
testWriteNewPhotoToAssetFile()7438     public void testWriteNewPhotoToAssetFile() throws Exception {
7439         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
7440         long contactId = queryContactId(rawContactId);
7441 
7442         // Load in a huge photo.
7443         final byte[] originalPhoto = loadPhotoFromResource(
7444                 R.drawable.earth_huge, PhotoSize.ORIGINAL);
7445 
7446         // Write it out.
7447         final Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon()
7448                 .appendPath(String.valueOf(rawContactId))
7449                 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
7450         writePhotoAsync(writeablePhotoUri, originalPhoto);
7451 
7452         // Check that the display photo and thumbnail have been set.
7453         String photoUri = null;
7454         for (int i = 0; i < 10 && photoUri == null; i++) {
7455             // Wait a tick for the photo processing to occur.
7456             Thread.sleep(100);
7457             photoUri = getStoredValue(
7458                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
7459                 Contacts.PHOTO_URI);
7460         }
7461 
7462         assertFalse(TextUtils.isEmpty(photoUri));
7463         String thumbnailUri = getStoredValue(
7464                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
7465                 Contacts.PHOTO_THUMBNAIL_URI);
7466         assertFalse(TextUtils.isEmpty(thumbnailUri));
7467         assertNotSame(photoUri, thumbnailUri);
7468 
7469         // Check the content of the display photo and thumbnail.
7470         EvenMoreAsserts.assertImageRawData(getContext(),
7471                 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO),
7472                 mResolver.openInputStream(Uri.parse(photoUri)));
7473         EvenMoreAsserts.assertImageRawData(getContext(),
7474                 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL),
7475                 mResolver.openInputStream(Uri.parse(thumbnailUri)));
7476     }
7477 
testWriteUpdatedPhotoToAssetFile()7478     public void testWriteUpdatedPhotoToAssetFile() throws Exception {
7479         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
7480         long contactId = queryContactId(rawContactId);
7481 
7482         // Insert a large photo first.
7483         insertPhoto(rawContactId, R.drawable.earth_large);
7484         String largeEarthPhotoUri = getStoredValue(
7485                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI);
7486 
7487         // Load in a huge photo.
7488         byte[] originalPhoto = loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL);
7489 
7490         // Write it out.
7491         Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon()
7492                 .appendPath(String.valueOf(rawContactId))
7493                 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
7494         writePhotoAsync(writeablePhotoUri, originalPhoto);
7495 
7496         // Allow a second for processing to occur.
7497         Thread.sleep(1000);
7498 
7499         // Check that the display photo URI has been modified.
7500         String hugeEarthPhotoUri = getStoredValue(
7501                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI);
7502         assertFalse(hugeEarthPhotoUri.equals(largeEarthPhotoUri));
7503 
7504         // Check the content of the display photo and thumbnail.
7505         String hugeEarthThumbnailUri = getStoredValue(
7506                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
7507                 Contacts.PHOTO_THUMBNAIL_URI);
7508         EvenMoreAsserts.assertImageRawData(getContext(),
7509                 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO),
7510                 mResolver.openInputStream(Uri.parse(hugeEarthPhotoUri)));
7511         EvenMoreAsserts.assertImageRawData(getContext(),
7512                 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL),
7513                 mResolver.openInputStream(Uri.parse(hugeEarthThumbnailUri)));
7514 
7515     }
7516 
writePhotoAsync(final Uri uri, final byte[] photoBytes)7517     private void writePhotoAsync(final Uri uri, final byte[] photoBytes) throws Exception {
7518         AsyncTask<Object, Object, Object> task = new AsyncTask<Object, Object, Object>() {
7519             @Override
7520             protected Object doInBackground(Object... params) {
7521                 OutputStream os;
7522                 try {
7523                     os = mResolver.openOutputStream(uri, "rw");
7524                     os.write(photoBytes);
7525                     os.close();
7526                     return null;
7527                 } catch (IOException ioe) {
7528                     throw new RuntimeException(ioe);
7529                 }
7530             }
7531         };
7532         task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[])null).get();
7533     }
7534 
testPhotoDimensionLimits()7535     public void testPhotoDimensionLimits() {
7536         ContentValues values = new ContentValues();
7537         values.put(DisplayPhoto.DISPLAY_MAX_DIM, 256);
7538         values.put(DisplayPhoto.THUMBNAIL_MAX_DIM, 96);
7539         assertStoredValues(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, values);
7540     }
7541 
testPhotoStoreCleanup()7542     public void testPhotoStoreCleanup() throws IOException {
7543         SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider;
7544         PhotoStore photoStore = provider.getPhotoStore();
7545 
7546         // Trigger an initial cleanup so another one won't happen while we're running this test.
7547         provider.cleanupPhotoStore();
7548 
7549         // Insert a couple of contacts with photos.
7550         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver);
7551         long contactId1 = queryContactId(rawContactId1);
7552         long dataId1 = ContentUris.parseId(insertPhoto(rawContactId1, R.drawable.earth_normal));
7553         long photoFileId1 =
7554                 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId1),
7555                         Photo.PHOTO_FILE_ID);
7556 
7557         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver);
7558         long contactId2 = queryContactId(rawContactId2);
7559         long dataId2 = ContentUris.parseId(insertPhoto(rawContactId2, R.drawable.earth_normal));
7560         long photoFileId2 =
7561                 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2),
7562                         Photo.PHOTO_FILE_ID);
7563 
7564         // Update the second raw contact with a different photo.
7565         ContentValues values = new ContentValues();
7566         values.put(Data.RAW_CONTACT_ID, rawContactId2);
7567         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7568         values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL));
7569         assertEquals(1, mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?",
7570                 new String[]{String.valueOf(dataId2)}));
7571         long replacementPhotoFileId =
7572                 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2),
7573                         Photo.PHOTO_FILE_ID);
7574 
7575         // Insert a third raw contact that has a bogus photo file ID.
7576         long bogusFileId = 1234567;
7577         long rawContactId3 = RawContactUtil.createRawContactWithName(mResolver);
7578         long contactId3 = queryContactId(rawContactId3);
7579         values.clear();
7580         values.put(Data.RAW_CONTACT_ID, rawContactId3);
7581         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
7582         values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_normal,
7583                 PhotoSize.THUMBNAIL));
7584         values.put(Photo.PHOTO_FILE_ID, bogusFileId);
7585         values.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
7586         mResolver.insert(Data.CONTENT_URI, values);
7587 
7588         // Insert a fourth raw contact with a stream item that has a photo, then remove that photo
7589         // from the photo store.
7590         Account socialAccount = new Account("social", "social");
7591         long rawContactId4 = RawContactUtil.createRawContactWithName(mResolver, socialAccount);
7592         Uri streamItemUri =
7593                 insertStreamItem(rawContactId4, buildGenericStreamItemValues(), socialAccount);
7594         long streamItemId = ContentUris.parseId(streamItemUri);
7595         Uri streamItemPhotoUri = insertStreamItemPhoto(
7596                 streamItemId, buildGenericStreamItemPhotoValues(0), socialAccount);
7597         long streamItemPhotoFileId = getStoredLongValue(streamItemPhotoUri,
7598                 StreamItemPhotos.PHOTO_FILE_ID);
7599         photoStore.remove(streamItemPhotoFileId);
7600 
7601         // Also insert a bogus photo that nobody is using.
7602         long bogusPhotoId = photoStore.insert(new PhotoProcessor(loadPhotoFromResource(
7603                 R.drawable.earth_huge, PhotoSize.ORIGINAL), 256, 96));
7604 
7605         // Manually trigger another cleanup in the provider.
7606         provider.cleanupPhotoStore();
7607 
7608         // The following things should have happened.
7609 
7610         // 1. Raw contact 1 and its photo remain unaffected.
7611         assertEquals(photoFileId1, (long) getStoredLongValue(
7612                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1),
7613                 Contacts.PHOTO_FILE_ID));
7614 
7615         // 2. Raw contact 2 retains its new photo.  The old one is deleted from the photo store.
7616         assertEquals(replacementPhotoFileId, (long) getStoredLongValue(
7617                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2),
7618                 Contacts.PHOTO_FILE_ID));
7619         assertNull(photoStore.get(photoFileId2));
7620 
7621         // 3. Raw contact 3 should have its photo file reference cleared.
7622         assertNull(getStoredValue(
7623                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId3),
7624                 Contacts.PHOTO_FILE_ID));
7625 
7626         // 4. The bogus photo that nobody was using should be cleared from the photo store.
7627         assertNull(photoStore.get(bogusPhotoId));
7628 
7629         // 5. The bogus stream item photo should be cleared from the stream item.
7630         assertStoredValues(Uri.withAppendedPath(
7631                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
7632                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
7633                 new ContentValues[0]);
7634     }
7635 
testPhotoStoreCleanupForProfile()7636     public void testPhotoStoreCleanupForProfile() {
7637         SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider;
7638         PhotoStore profilePhotoStore = provider.getProfilePhotoStore();
7639 
7640         // Trigger an initial cleanup so another one won't happen while we're running this test.
7641         provider.switchToProfileModeForTest();
7642         provider.cleanupPhotoStore();
7643 
7644         // Create the profile contact and add a photo.
7645         Account socialAccount = new Account("social", "social");
7646         ContentValues values = new ContentValues();
7647         values.put(RawContacts.ACCOUNT_NAME, socialAccount.name);
7648         values.put(RawContacts.ACCOUNT_TYPE, socialAccount.type);
7649         long profileRawContactId = createBasicProfileContact(values);
7650         long profileContactId = queryContactId(profileRawContactId);
7651         long dataId = ContentUris.parseId(
7652                 insertPhoto(profileRawContactId, R.drawable.earth_normal));
7653         long profilePhotoFileId =
7654                 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
7655                         Photo.PHOTO_FILE_ID);
7656 
7657         // Also add a stream item with a photo.
7658         Uri streamItemUri =
7659                 insertStreamItem(profileRawContactId, buildGenericStreamItemValues(),
7660                         socialAccount);
7661         long streamItemId = ContentUris.parseId(streamItemUri);
7662         Uri streamItemPhotoUri = insertStreamItemPhoto(
7663                 streamItemId, buildGenericStreamItemPhotoValues(0), socialAccount);
7664         long streamItemPhotoFileId = getStoredLongValue(streamItemPhotoUri,
7665                 StreamItemPhotos.PHOTO_FILE_ID);
7666 
7667         // Remove the stream item photo and the profile photo.
7668         profilePhotoStore.remove(profilePhotoFileId);
7669         profilePhotoStore.remove(streamItemPhotoFileId);
7670 
7671         // Manually trigger another cleanup in the provider.
7672         provider.switchToProfileModeForTest();
7673         provider.cleanupPhotoStore();
7674 
7675         // The following things should have happened.
7676 
7677         // The stream item photo should have been removed.
7678         assertStoredValues(Uri.withAppendedPath(
7679                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
7680                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
7681                 new ContentValues[0]);
7682 
7683         // The profile photo should have been cleared.
7684         assertNull(getStoredValue(
7685                 ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId),
7686                 Contacts.PHOTO_FILE_ID));
7687 
7688     }
7689 
testCleanupDanglingContacts_noDanglingContacts()7690     public void testCleanupDanglingContacts_noDanglingContacts() throws Exception {
7691         SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider;
7692         RawContactUtil.createRawContactWithName(mResolver, "A", "B");
7693         RawContactUtil.createRawContactWithName(mResolver, "C", "D");
7694 
7695         provider.cleanupDanglingContacts();
7696 
7697         Cursor contactCursor = mResolver.query(Contacts.CONTENT_URI, null, null, null, null);
7698         Cursor rawContactCursor = mResolver.query(RawContacts.CONTENT_URI, null, null, null, null);
7699 
7700         // No contacts should be deleted
7701         assertEquals(2, contactCursor.getCount());
7702         assertEquals(2, rawContactCursor.getCount());
7703     }
7704 
testCleanupDanglingContacts_singleDanglingContacts()7705     public void testCleanupDanglingContacts_singleDanglingContacts() throws Exception {
7706         SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider;
7707         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "A", "B");
7708 
7709         // Change the contact_id to create dangling contact.
7710         SQLiteDatabase db = provider.getDatabaseHelper().getWritableDatabase();
7711         db.execSQL("UPDATE raw_contacts SET contact_id = 99999 WHERE _id = " + rawContactId + ";");
7712 
7713         provider.cleanupDanglingContacts();
7714 
7715         // Dangling contact should be deleted from contacts table.
7716         assertEquals(0, mResolver.query(Contacts.CONTENT_URI, null, null, null, null).getCount());
7717     }
7718 
testCleanupDanglingContacts_multipleDanglingContacts()7719     public void testCleanupDanglingContacts_multipleDanglingContacts() throws Exception {
7720         SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider;
7721         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "A", "B");
7722         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "C", "D");
7723         RawContactUtil.createRawContactWithName(mResolver, "E", "F");
7724 
7725         final ContactsDatabaseHelper helper = provider.getDatabaseHelper();
7726         SQLiteDatabase db = helper.getWritableDatabase();
7727 
7728         // Change contact_id of RawContact1 and RawContact2 to create dangling contacts.
7729         db.execSQL("UPDATE raw_contacts SET contact_id = 99998 WHERE _id = " + rawContactId1 + ";");
7730         db.execSQL("UPDATE raw_contacts SET contact_id = 99999 WHERE _id = " + rawContactId2 + ";");
7731 
7732         provider.cleanupDanglingContacts();
7733 
7734         // Should only be one contact left in the contacts table.
7735         // RawContact1 and RawContact2 should be deleted from the contacts table.
7736         assertEquals(1, mResolver.query(Contacts.CONTENT_URI, null, null, null, null).getCount());
7737     }
7738 
testOverwritePhotoWithThumbnail()7739     public void testOverwritePhotoWithThumbnail() throws IOException {
7740         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
7741         long contactId = queryContactId(rawContactId);
7742         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7743 
7744         // Write a regular-size photo.
7745         long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
7746         Long photoFileId = getStoredLongValue(contactUri, Contacts.PHOTO_FILE_ID);
7747         assertTrue(photoFileId != null && photoFileId > 0);
7748 
7749         // Now overwrite the photo with a thumbnail-sized photo.
7750         ContentValues update = new ContentValues();
7751         update.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_small, PhotoSize.ORIGINAL));
7752         mResolver.update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), update, null, null);
7753 
7754         // Photo file ID should have been nulled out, and the photo URI should be the same as the
7755         // thumbnail URI.
7756         assertNull(getStoredValue(contactUri, Contacts.PHOTO_FILE_ID));
7757         String photoUri = getStoredValue(contactUri, Contacts.PHOTO_URI);
7758         String thumbnailUri = getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI);
7759         assertEquals(photoUri, thumbnailUri);
7760 
7761         // Retrieving the photo URI should get the thumbnail content.
7762         EvenMoreAsserts.assertImageRawData(getContext(),
7763                 loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL),
7764                 mResolver.openInputStream(Uri.parse(photoUri)));
7765     }
7766 
testUpdateRawContactSetStarred()7767     public void testUpdateRawContactSetStarred() {
7768         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver);
7769         Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
7770         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver);
7771         Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
7772         setAggregationException(
7773                 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
7774 
7775         long contactId = queryContactId(rawContactId1);
7776         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
7777         assertStoredValue(contactUri, Contacts.STARRED, "0");
7778 
7779         assertDirty(rawContactUri1, true);
7780         assertDirty(rawContactUri2, true);
7781         clearDirty(rawContactUri1);
7782         clearDirty(rawContactUri2);
7783 
7784         ContentValues values = new ContentValues();
7785         values.put(RawContacts.STARRED, "1");
7786 
7787         mResolver.update(rawContactUri1, values, null, null);
7788 
7789         assertStoredValue(rawContactUri1, RawContacts.STARRED, "1");
7790         assertStoredValue(rawContactUri2, RawContacts.STARRED, "0");
7791         assertStoredValue(contactUri, Contacts.STARRED, "1");
7792         assertDirty(rawContactUri1, true);
7793         assertNetworkNotified(true);
7794 
7795         clearDirty(rawContactUri1);
7796         values.put(RawContacts.STARRED, "0");
7797         mResolver.update(rawContactUri1, values, null, null);
7798 
7799         assertStoredValue(rawContactUri1, RawContacts.STARRED, "0");
7800         assertStoredValue(rawContactUri2, RawContacts.STARRED, "0");
7801         assertStoredValue(contactUri, Contacts.STARRED, "0");
7802         assertDirty(rawContactUri1, true);
7803         assertNetworkNotified(true);
7804 
7805         clearDirty(rawContactUri1);
7806         values.put(Contacts.STARRED, "1");
7807         mResolver.update(contactUri, values, null, null);
7808 
7809         assertStoredValue(rawContactUri1, RawContacts.STARRED, "1");
7810         assertStoredValue(rawContactUri2, RawContacts.STARRED, "1");
7811         assertStoredValue(contactUri, Contacts.STARRED, "1");
7812         assertDirty(rawContactUri1, true);
7813         assertNetworkNotified(true);
7814     }
7815 
testUpdateContactOptionsSetStarred()7816     public void testUpdateContactOptionsSetStarred() {
7817         long rawContactId = RawContactUtil.createRawContact(mResolver);
7818         long contactId = queryContactId(rawContactId);
7819         String lookupKey = queryLookupKey(contactId);
7820         ContentValues values =new ContentValues();
7821         values.put(Contacts.STARRED, 1);
7822 
7823         Uri contactLookupUri = ContentUris.withAppendedId(
7824             Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
7825         mResolver.update(contactLookupUri, values, null, null);
7826         assertNetworkNotified(true);
7827     }
7828 
testSetAndClearSuperPrimaryEmail()7829     public void testSetAndClearSuperPrimaryEmail() {
7830         long rawContactId1 = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
7831         Uri mailUri11 = insertEmail(rawContactId1, "test1@domain1.com");
7832         Uri mailUri12 = insertEmail(rawContactId1, "test2@domain1.com");
7833 
7834         long rawContactId2 = RawContactUtil.createRawContact(mResolver, new Account("b", "b"));
7835         Uri mailUri21 = insertEmail(rawContactId2, "test1@domain2.com");
7836         Uri mailUri22 = insertEmail(rawContactId2, "test2@domain2.com");
7837 
7838         assertStoredValue(mailUri11, Data.IS_PRIMARY, 0);
7839         assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0);
7840         assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
7841         assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
7842         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
7843         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
7844         assertStoredValue(mailUri22, Data.IS_PRIMARY, 0);
7845         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 0);
7846 
7847         // Set super primary on the first pair, primary on the second
7848         {
7849             ContentValues values = new ContentValues();
7850             values.put(Data.IS_SUPER_PRIMARY, 1);
7851             mResolver.update(mailUri11, values, null, null);
7852         }
7853         {
7854             ContentValues values = new ContentValues();
7855             values.put(Data.IS_SUPER_PRIMARY, 1);
7856             mResolver.update(mailUri22, values, null, null);
7857         }
7858 
7859         assertStoredValue(mailUri11, Data.IS_PRIMARY, 1);
7860         assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 1);
7861         assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
7862         assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
7863         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
7864         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
7865         assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
7866         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
7867 
7868         // Clear primary on the first pair, make sure second is not affected and super_primary is
7869         // also cleared
7870         {
7871             ContentValues values = new ContentValues();
7872             values.put(Data.IS_PRIMARY, 0);
7873             mResolver.update(mailUri11, values, null, null);
7874         }
7875 
7876         assertStoredValue(mailUri11, Data.IS_PRIMARY, 0);
7877         assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0);
7878         assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
7879         assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
7880         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
7881         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
7882         assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
7883         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
7884 
7885         // Ensure that we can only clear super_primary, if we specify the correct data row
7886         {
7887             ContentValues values = new ContentValues();
7888             values.put(Data.IS_SUPER_PRIMARY, 0);
7889             mResolver.update(mailUri21, values, null, null);
7890         }
7891 
7892         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
7893         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
7894         assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
7895         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
7896 
7897         // Ensure that we can only clear primary, if we specify the correct data row
7898         {
7899             ContentValues values = new ContentValues();
7900             values.put(Data.IS_PRIMARY, 0);
7901             mResolver.update(mailUri21, values, null, null);
7902         }
7903 
7904         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
7905         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
7906         assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
7907         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
7908 
7909         // Now clear super-primary for real
7910         {
7911             ContentValues values = new ContentValues();
7912             values.put(Data.IS_SUPER_PRIMARY, 0);
7913             mResolver.update(mailUri22, values, null, null);
7914         }
7915 
7916         assertStoredValue(mailUri11, Data.IS_PRIMARY, 0);
7917         assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0);
7918         assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
7919         assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
7920         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
7921         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
7922         assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
7923         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 0);
7924     }
7925 
7926     /**
7927      * Common function for the testNewPrimaryIn* functions. Its four configurations
7928      * are each called from its own test
7929      */
testChangingPrimary(boolean inUpdate, boolean withSuperPrimary)7930     public void testChangingPrimary(boolean inUpdate, boolean withSuperPrimary) {
7931         long rawContactId = RawContactUtil.createRawContact(mResolver, new Account("a", "a"));
7932         Uri mailUri1 = insertEmail(rawContactId, "test1@domain1.com", true);
7933 
7934         if (withSuperPrimary) {
7935             final ContentValues values = new ContentValues();
7936             values.put(Data.IS_SUPER_PRIMARY, 1);
7937             mResolver.update(mailUri1, values, null, null);
7938         }
7939 
7940         assertStoredValue(mailUri1, Data.IS_PRIMARY, 1);
7941         assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0);
7942 
7943         // Insert another item
7944         final Uri mailUri2;
7945         if (inUpdate) {
7946             mailUri2 = insertEmail(rawContactId, "test2@domain1.com");
7947 
7948             assertStoredValue(mailUri1, Data.IS_PRIMARY, 1);
7949             assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0);
7950             assertStoredValue(mailUri2, Data.IS_PRIMARY, 0);
7951             assertStoredValue(mailUri2, Data.IS_SUPER_PRIMARY, 0);
7952 
7953             final ContentValues values = new ContentValues();
7954             values.put(Data.IS_PRIMARY, 1);
7955             mResolver.update(mailUri2, values, null, null);
7956         } else {
7957             // directly add as default
7958             mailUri2 = insertEmail(rawContactId, "test2@domain1.com", true);
7959         }
7960 
7961         // Ensure that primary has been unset on the first
7962         // If withSuperPrimary is set, also ensure that is has been moved to the new item
7963         assertStoredValue(mailUri1, Data.IS_PRIMARY, 0);
7964         assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, 0);
7965         assertStoredValue(mailUri2, Data.IS_PRIMARY, 1);
7966         assertStoredValue(mailUri2, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0);
7967     }
7968 
testNewPrimaryInInsert()7969     public void testNewPrimaryInInsert() {
7970         testChangingPrimary(false, false);
7971     }
7972 
testNewPrimaryInInsertWithSuperPrimary()7973     public void testNewPrimaryInInsertWithSuperPrimary() {
7974         testChangingPrimary(false, true);
7975     }
7976 
testNewPrimaryInUpdate()7977     public void testNewPrimaryInUpdate() {
7978         testChangingPrimary(true, false);
7979     }
7980 
testNewPrimaryInUpdateWithSuperPrimary()7981     public void testNewPrimaryInUpdateWithSuperPrimary() {
7982         testChangingPrimary(true, true);
7983     }
7984 
testContactSortOrder()7985     public void testContactSortOrder() {
7986         assertEquals(ContactsColumns.PHONEBOOK_BUCKET_PRIMARY + ", "
7987                      + Contacts.SORT_KEY_PRIMARY,
7988                      ContactsProvider2.getLocalizedSortOrder(Contacts.SORT_KEY_PRIMARY));
7989         assertEquals(ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE + ", "
7990                      + Contacts.SORT_KEY_ALTERNATIVE,
7991                      ContactsProvider2.getLocalizedSortOrder(Contacts.SORT_KEY_ALTERNATIVE));
7992         assertEquals(ContactsColumns.PHONEBOOK_BUCKET_PRIMARY + " DESC, "
7993                      + Contacts.SORT_KEY_PRIMARY + " DESC",
7994                      ContactsProvider2.getLocalizedSortOrder(Contacts.SORT_KEY_PRIMARY + " DESC"));
7995         String suffix = " COLLATE LOCALIZED DESC";
7996         assertEquals(ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE + suffix
7997                      + ", " + Contacts.SORT_KEY_ALTERNATIVE + suffix,
7998                      ContactsProvider2.getLocalizedSortOrder(Contacts.SORT_KEY_ALTERNATIVE
7999                                                              + suffix));
8000     }
8001 
testContactCounts()8002     public void testContactCounts() {
8003         Uri uri = Contacts.CONTENT_URI.buildUpon()
8004                 .appendQueryParameter(Contacts.EXTRA_ADDRESS_BOOK_INDEX, "true").build();
8005 
8006         RawContactUtil.createRawContact(mResolver);
8007         RawContactUtil.createRawContactWithName(mResolver, "James", "Sullivan");
8008         RawContactUtil.createRawContactWithName(mResolver, "The Abominable", "Snowman");
8009         RawContactUtil.createRawContactWithName(mResolver, "Mike", "Wazowski");
8010         RawContactUtil.createRawContactWithName(mResolver, "randall", "boggs");
8011         RawContactUtil.createRawContactWithName(mResolver, "Boo", null);
8012         RawContactUtil.createRawContactWithName(mResolver, "Mary", null);
8013         RawContactUtil.createRawContactWithName(mResolver, "Roz", null);
8014         // Contacts with null display names get sorted to the end (using the number bucket)
8015         RawContactUtil.createRawContactWithName(mResolver, null, null);
8016 
8017         Cursor cursor = mResolver.query(uri,
8018                 new String[]{Contacts.DISPLAY_NAME},
8019                 null, null, Contacts.SORT_KEY_PRIMARY);
8020 
8021         assertFirstLetterValues(cursor, "B", "J", "M", "R", "T", "#");
8022         assertFirstLetterCounts(cursor,  1,   1,   2,   2,   1,   2);
8023         cursor.close();
8024 
8025         cursor = mResolver.query(uri,
8026                 new String[]{Contacts.DISPLAY_NAME},
8027                 null, null, Contacts.SORT_KEY_ALTERNATIVE + " COLLATE LOCALIZED DESC");
8028 
8029         assertFirstLetterValues(cursor, "#", "W", "S", "R", "M", "B");
8030         assertFirstLetterCounts(cursor,  2,   1,   2,   1,   1,   2);
8031         cursor.close();
8032     }
8033 
assertFirstLetterValues(Cursor cursor, String... expected)8034     private void assertFirstLetterValues(Cursor cursor, String... expected) {
8035         String[] actual = cursor.getExtras()
8036                 .getStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
8037         MoreAsserts.assertEquals(expected, actual);
8038     }
8039 
assertFirstLetterCounts(Cursor cursor, int... expected)8040     private void assertFirstLetterCounts(Cursor cursor, int... expected) {
8041         int[] actual = cursor.getExtras()
8042                 .getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
8043         MoreAsserts.assertEquals(expected, actual);
8044     }
8045 
testReadBooleanQueryParameter()8046     public void testReadBooleanQueryParameter() {
8047         assertBooleanUriParameter("foo:bar", "bool", true, true);
8048         assertBooleanUriParameter("foo:bar", "bool", false, false);
8049         assertBooleanUriParameter("foo:bar?bool=0", "bool", true, false);
8050         assertBooleanUriParameter("foo:bar?bool=1", "bool", false, true);
8051         assertBooleanUriParameter("foo:bar?bool=false", "bool", true, false);
8052         assertBooleanUriParameter("foo:bar?bool=true", "bool", false, true);
8053         assertBooleanUriParameter("foo:bar?bool=FaLsE", "bool", true, false);
8054         assertBooleanUriParameter("foo:bar?bool=false&some=some", "bool", true, false);
8055         assertBooleanUriParameter("foo:bar?bool=1&some=some", "bool", false, true);
8056         assertBooleanUriParameter("foo:bar?some=bool", "bool", true, true);
8057         assertBooleanUriParameter("foo:bar?bool", "bool", true, true);
8058     }
8059 
assertBooleanUriParameter(String uriString, String parameter, boolean defaultValue, boolean expectedValue)8060     private void assertBooleanUriParameter(String uriString, String parameter,
8061             boolean defaultValue, boolean expectedValue) {
8062         assertEquals(expectedValue, ContactsProvider2.readBooleanQueryParameter(
8063                 Uri.parse(uriString), parameter, defaultValue));
8064     }
8065 
testGetQueryParameter()8066     public void testGetQueryParameter() {
8067         assertQueryParameter("foo:bar", "param", null);
8068         assertQueryParameter("foo:bar?param", "param", null);
8069         assertQueryParameter("foo:bar?param=", "param", "");
8070         assertQueryParameter("foo:bar?param=val", "param", "val");
8071         assertQueryParameter("foo:bar?param=val&some=some", "param", "val");
8072         assertQueryParameter("foo:bar?some=some&param=val", "param", "val");
8073         assertQueryParameter("foo:bar?some=some&param=val&else=else", "param", "val");
8074         assertQueryParameter("foo:bar?param=john%40doe.com", "param", "john@doe.com");
8075         assertQueryParameter("foo:bar?some_param=val", "param", null);
8076         assertQueryParameter("foo:bar?some_param=val1&param=val2", "param", "val2");
8077         assertQueryParameter("foo:bar?some_param=val1&param=", "param", "");
8078         assertQueryParameter("foo:bar?some_param=val1&param", "param", null);
8079         assertQueryParameter("foo:bar?some_param=val1&another_param=val2&param=val3",
8080                 "param", "val3");
8081         assertQueryParameter("foo:bar?some_param=val1&param=val2&some_param=val3",
8082                 "param", "val2");
8083         assertQueryParameter("foo:bar?param=val1&some_param=val2", "param", "val1");
8084         assertQueryParameter("foo:bar?p=val1&pp=val2", "p", "val1");
8085         assertQueryParameter("foo:bar?pp=val1&p=val2", "p", "val2");
8086         assertQueryParameter("foo:bar?ppp=val1&pp=val2&p=val3", "p", "val3");
8087         assertQueryParameter("foo:bar?ppp=val&", "p", null);
8088     }
8089 
testMissingAccountTypeParameter()8090     public void testMissingAccountTypeParameter() {
8091         // Try querying for RawContacts only using ACCOUNT_NAME
8092         final Uri queryUri = RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(
8093                 RawContacts.ACCOUNT_NAME, "lolwut").build();
8094         try {
8095             final Cursor cursor = mResolver.query(queryUri, null, null, null, null);
8096             fail("Able to query with incomplete account query parameters");
8097         } catch (IllegalArgumentException e) {
8098             // Expected behavior.
8099         }
8100     }
8101 
testInsertInconsistentAccountType()8102     public void testInsertInconsistentAccountType() {
8103         // Try inserting RawContact with inconsistent Accounts
8104         final Account red = new Account("red", "red");
8105         final Account blue = new Account("blue", "blue");
8106 
8107         final ContentValues values = new ContentValues();
8108         values.put(RawContacts.ACCOUNT_NAME, red.name);
8109         values.put(RawContacts.ACCOUNT_TYPE, red.type);
8110 
8111         final Uri insertUri = TestUtil.maybeAddAccountQueryParameters(RawContacts.CONTENT_URI,
8112                 blue);
8113         try {
8114             mResolver.insert(insertUri, values);
8115             fail("Able to insert RawContact with inconsistent account details");
8116         } catch (IllegalArgumentException e) {
8117             // Expected behavior.
8118         }
8119     }
8120 
testProviderStatusNoContactsNoAccounts()8121     public void testProviderStatusNoContactsNoAccounts() throws Exception {
8122         assertProviderStatus(ProviderStatus.STATUS_EMPTY);
8123     }
8124 
testProviderStatusOnlyLocalContacts()8125     public void testProviderStatusOnlyLocalContacts() throws Exception {
8126         long rawContactId = RawContactUtil.createRawContact(mResolver);
8127         assertProviderStatus(ProviderStatus.STATUS_NORMAL);
8128         mResolver.delete(
8129                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), null, null);
8130         assertProviderStatus(ProviderStatus.STATUS_EMPTY);
8131     }
8132 
testProviderStatusWithAccounts()8133     public void testProviderStatusWithAccounts() throws Exception {
8134         assertProviderStatus(ProviderStatus.STATUS_EMPTY);
8135         mActor.setAccounts(new Account[]{TestUtil.ACCOUNT_1});
8136         ((ContactsProvider2)getProvider()).onAccountsUpdated(new Account[]{TestUtil.ACCOUNT_1});
8137         assertProviderStatus(ProviderStatus.STATUS_NORMAL);
8138         mActor.setAccounts(new Account[0]);
8139         ((ContactsProvider2)getProvider()).onAccountsUpdated(new Account[0]);
8140         assertProviderStatus(ProviderStatus.STATUS_EMPTY);
8141     }
8142 
assertProviderStatus(int expectedProviderStatus)8143     private void assertProviderStatus(int expectedProviderStatus) {
8144         Cursor cursor = mResolver.query(ProviderStatus.CONTENT_URI,
8145                 new String[]{ProviderStatus.STATUS}, null, null,
8146                 null);
8147         assertTrue(cursor.moveToFirst());
8148         assertEquals(expectedProviderStatus, cursor.getInt(0));
8149         cursor.close();
8150     }
8151 
testProperties()8152     public void testProperties() throws Exception {
8153         ContactsProvider2 provider = (ContactsProvider2)getProvider();
8154         ContactsDatabaseHelper helper = (ContactsDatabaseHelper)provider.getDatabaseHelper();
8155         assertNull(helper.getProperty("non-existent", null));
8156         assertEquals("default", helper.getProperty("non-existent", "default"));
8157 
8158         helper.setProperty("existent1", "string1");
8159         helper.setProperty("existent2", "string2");
8160         assertEquals("string1", helper.getProperty("existent1", "default"));
8161         assertEquals("string2", helper.getProperty("existent2", "default"));
8162         helper.setProperty("existent1", null);
8163         assertEquals("default", helper.getProperty("existent1", "default"));
8164     }
8165 
8166     private class VCardTestUriCreator {
8167         private String mLookup1;
8168         private String mLookup2;
8169 
VCardTestUriCreator(String lookup1, String lookup2)8170         public VCardTestUriCreator(String lookup1, String lookup2) {
8171             super();
8172             mLookup1 = lookup1;
8173             mLookup2 = lookup2;
8174         }
8175 
getUri1()8176         public Uri getUri1() {
8177             return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1);
8178         }
8179 
getUri2()8180         public Uri getUri2() {
8181             return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2);
8182         }
8183 
getCombinedUri()8184         public Uri getCombinedUri() {
8185             return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI,
8186                     Uri.encode(mLookup1 + ":" + mLookup2));
8187         }
8188     }
8189 
createVCardTestContacts()8190     private VCardTestUriCreator createVCardTestContacts() {
8191         final long rawContactId1 = RawContactUtil.createRawContact(mResolver, mAccount,
8192                 RawContacts.SOURCE_ID, "4:12");
8193         DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Doe");
8194 
8195         final long rawContactId2 = RawContactUtil.createRawContact(mResolver, mAccount,
8196                 RawContacts.SOURCE_ID, "3:4%121");
8197         DataUtil.insertStructuredName(mResolver, rawContactId2, "Jane", "Doh");
8198 
8199         final long contactId1 = queryContactId(rawContactId1);
8200         final long contactId2 = queryContactId(rawContactId2);
8201         final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1);
8202         final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2);
8203         final String lookup1 =
8204             Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2));
8205         final String lookup2 =
8206             Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2));
8207         return new VCardTestUriCreator(lookup1, lookup2);
8208     }
8209 
testQueryMultiVCard()8210     public void testQueryMultiVCard() {
8211         // No need to create any contacts here, because the query for multiple vcards
8212         // does not go into the database at all
8213         Uri uri = Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, Uri.encode("123:456"));
8214         Cursor cursor = mResolver.query(uri, null, null, null, null);
8215         assertEquals(1, cursor.getCount());
8216         assertTrue(cursor.moveToFirst());
8217         assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
8218         String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
8219 
8220         // The resulting name contains date and time. Ensure that before and after are correct
8221         assertTrue(filename.startsWith("vcards_"));
8222         assertTrue(filename.endsWith(".vcf"));
8223         cursor.close();
8224     }
8225 
testQueryFileSingleVCard()8226     public void testQueryFileSingleVCard() {
8227         final VCardTestUriCreator contacts = createVCardTestContacts();
8228 
8229         {
8230             Cursor cursor = mResolver.query(contacts.getUri1(), null, null, null, null);
8231             assertEquals(1, cursor.getCount());
8232             assertTrue(cursor.moveToFirst());
8233             assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
8234             String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
8235             assertEquals("John Doe.vcf", filename);
8236             cursor.close();
8237         }
8238 
8239         {
8240             Cursor cursor = mResolver.query(contacts.getUri2(), null, null, null, null);
8241             assertEquals(1, cursor.getCount());
8242             assertTrue(cursor.moveToFirst());
8243             assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
8244             String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
8245             assertEquals("Jane Doh.vcf", filename);
8246             cursor.close();
8247         }
8248     }
8249 
testQueryFileProfileVCard()8250     public void testQueryFileProfileVCard() {
8251         createBasicProfileContact(new ContentValues());
8252         Cursor cursor = mResolver.query(Profile.CONTENT_VCARD_URI, null, null, null, null);
8253         assertEquals(1, cursor.getCount());
8254         assertTrue(cursor.moveToFirst());
8255         assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
8256         String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
8257         assertEquals("Mia Prophyl.vcf", filename);
8258         cursor.close();
8259     }
8260 
testOpenAssetFileMultiVCard()8261     public void testOpenAssetFileMultiVCard() throws IOException {
8262         final VCardTestUriCreator contacts = createVCardTestContacts();
8263 
8264         final AssetFileDescriptor descriptor =
8265             mResolver.openAssetFileDescriptor(contacts.getCombinedUri(), "r");
8266         final FileInputStream inputStream = descriptor.createInputStream();
8267         String data = readToEnd(inputStream);
8268         inputStream.close();
8269         descriptor.close();
8270 
8271         // Ensure that the resulting VCard has both contacts
8272         assertTrue(data.contains("N:Doe;John;;;"));
8273         assertTrue(data.contains("N:Doh;Jane;;;"));
8274     }
8275 
testOpenAssetFileSingleVCard()8276     public void testOpenAssetFileSingleVCard() throws IOException {
8277         final VCardTestUriCreator contacts = createVCardTestContacts();
8278 
8279         // Ensure that the right VCard is being created in each case
8280         {
8281             final AssetFileDescriptor descriptor =
8282                 mResolver.openAssetFileDescriptor(contacts.getUri1(), "r");
8283             final FileInputStream inputStream = descriptor.createInputStream();
8284             final String data = readToEnd(inputStream);
8285             inputStream.close();
8286             descriptor.close();
8287 
8288             assertTrue(data.contains("N:Doe;John;;;"));
8289             assertFalse(data.contains("N:Doh;Jane;;;"));
8290         }
8291 
8292         {
8293             final AssetFileDescriptor descriptor =
8294                 mResolver.openAssetFileDescriptor(contacts.getUri2(), "r");
8295             final FileInputStream inputStream = descriptor.createInputStream();
8296             final String data = readToEnd(inputStream);
8297             inputStream.close();
8298             descriptor.close();
8299 
8300             assertFalse(data.contains("N:Doe;John;;;"));
8301             assertTrue(data.contains("N:Doh;Jane;;;"));
8302         }
8303     }
8304 
testAutoGroupMembership()8305     public void testAutoGroupMembership() {
8306         long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */);
8307         long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
8308         long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false /* favorite */);
8309         long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, false/* favorite */);
8310         long r1 = RawContactUtil.createRawContact(mResolver, mAccount);
8311         long r2 = RawContactUtil.createRawContact(mResolver, mAccountTwo);
8312         long r3 = RawContactUtil.createRawContact(mResolver, null);
8313 
8314         Cursor c = queryGroupMemberships(mAccount);
8315         try {
8316             assertTrue(c.moveToNext());
8317             assertEquals(g1, c.getLong(0));
8318             assertEquals(r1, c.getLong(1));
8319             assertFalse(c.moveToNext());
8320         } finally {
8321             c.close();
8322         }
8323 
8324         c = queryGroupMemberships(mAccountTwo);
8325         try {
8326             assertTrue(c.moveToNext());
8327             assertEquals(g3, c.getLong(0));
8328             assertEquals(r2, c.getLong(1));
8329             assertFalse(c.moveToNext());
8330         } finally {
8331             c.close();
8332         }
8333     }
8334 
testNoAutoAddMembershipAfterGroupCreation()8335     public void testNoAutoAddMembershipAfterGroupCreation() {
8336         long r1 = RawContactUtil.createRawContact(mResolver, mAccount);
8337         long r2 = RawContactUtil.createRawContact(mResolver, mAccount);
8338         long r3 = RawContactUtil.createRawContact(mResolver, mAccount);
8339         long r4 = RawContactUtil.createRawContact(mResolver, mAccountTwo);
8340         long r5 = RawContactUtil.createRawContact(mResolver, mAccountTwo);
8341         long r6 = RawContactUtil.createRawContact(mResolver, null);
8342 
8343         assertNoRowsAndClose(queryGroupMemberships(mAccount));
8344         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8345 
8346         long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */);
8347         long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
8348         long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false/* favorite */);
8349 
8350         assertNoRowsAndClose(queryGroupMemberships(mAccount));
8351         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8352     }
8353 
8354     // create some starred and non-starred contacts, some associated with account, some not
8355     // favorites group created
8356     // the starred contacts should be added to group
8357     // favorites group removed
8358     // no change to starred status
testFavoritesMembershipAfterGroupCreation()8359     public void testFavoritesMembershipAfterGroupCreation() {
8360         long r1 = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.STARRED, "1");
8361         long r2 = RawContactUtil.createRawContact(mResolver, mAccount);
8362         long r3 = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.STARRED, "1");
8363         long r4 = RawContactUtil.createRawContact(mResolver, mAccountTwo, RawContacts.STARRED, "1");
8364         long r5 = RawContactUtil.createRawContact(mResolver, mAccountTwo);
8365         long r6 = RawContactUtil.createRawContact(mResolver, null, RawContacts.STARRED, "1");
8366         long r7 = RawContactUtil.createRawContact(mResolver, null);
8367 
8368         assertNoRowsAndClose(queryGroupMemberships(mAccount));
8369         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8370 
8371         long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
8372         long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
8373         long g3 = createGroup(mAccountTwo, "g3", "t3", 0, false /* autoAdd */, false/* favorite */);
8374 
8375         assertTrue(queryRawContactIsStarred(r1));
8376         assertFalse(queryRawContactIsStarred(r2));
8377         assertTrue(queryRawContactIsStarred(r3));
8378         assertTrue(queryRawContactIsStarred(r4));
8379         assertFalse(queryRawContactIsStarred(r5));
8380         assertTrue(queryRawContactIsStarred(r6));
8381         assertFalse(queryRawContactIsStarred(r7));
8382 
8383         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8384         Cursor c = queryGroupMemberships(mAccount);
8385         try {
8386             assertTrue(c.moveToNext());
8387             assertEquals(g1, c.getLong(0));
8388             assertEquals(r1, c.getLong(1));
8389             assertTrue(c.moveToNext());
8390             assertEquals(g1, c.getLong(0));
8391             assertEquals(r3, c.getLong(1));
8392             assertFalse(c.moveToNext());
8393         } finally {
8394             c.close();
8395         }
8396 
8397         updateItem(RawContacts.CONTENT_URI, r6,
8398                 RawContacts.ACCOUNT_NAME, mAccount.name,
8399                 RawContacts.ACCOUNT_TYPE, mAccount.type);
8400         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8401         c = queryGroupMemberships(mAccount);
8402         try {
8403             assertTrue(c.moveToNext());
8404             assertEquals(g1, c.getLong(0));
8405             assertEquals(r1, c.getLong(1));
8406             assertTrue(c.moveToNext());
8407             assertEquals(g1, c.getLong(0));
8408             assertEquals(r3, c.getLong(1));
8409             assertTrue(c.moveToNext());
8410             assertEquals(g1, c.getLong(0));
8411             assertEquals(r6, c.getLong(1));
8412             assertFalse(c.moveToNext());
8413         } finally {
8414             c.close();
8415         }
8416 
8417         mResolver.delete(ContentUris.withAppendedId(Groups.CONTENT_URI, g1), null, null);
8418 
8419         assertNoRowsAndClose(queryGroupMemberships(mAccount));
8420         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8421 
8422         assertTrue(queryRawContactIsStarred(r1));
8423         assertFalse(queryRawContactIsStarred(r2));
8424         assertTrue(queryRawContactIsStarred(r3));
8425         assertTrue(queryRawContactIsStarred(r4));
8426         assertFalse(queryRawContactIsStarred(r5));
8427         assertTrue(queryRawContactIsStarred(r6));
8428         assertFalse(queryRawContactIsStarred(r7));
8429     }
8430 
testFavoritesGroupMembershipChangeAfterStarChange()8431     public void testFavoritesGroupMembershipChangeAfterStarChange() {
8432         long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
8433         long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */);
8434         long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */);
8435         long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */);
8436         long r1 = RawContactUtil.createRawContact(mResolver, mAccount, RawContacts.STARRED, "1");
8437         long r2 = RawContactUtil.createRawContact(mResolver, mAccount);
8438         long r3 = RawContactUtil.createRawContact(mResolver, mAccountTwo);
8439 
8440         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8441         Cursor c = queryGroupMemberships(mAccount);
8442         try {
8443             assertTrue(c.moveToNext());
8444             assertEquals(g1, c.getLong(0));
8445             assertEquals(r1, c.getLong(1));
8446             assertFalse(c.moveToNext());
8447         } finally {
8448             c.close();
8449         }
8450 
8451         // remove the star from r1
8452         assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0"));
8453 
8454         // Since no raw contacts are starred, there should be no group memberships.
8455         assertNoRowsAndClose(queryGroupMemberships(mAccount));
8456         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8457 
8458         // mark r1 as starred
8459         assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "1"));
8460         // Now that r1 is starred it should have a membership in the one groups from mAccount
8461         // that is marked as a favorite.
8462         // There should be no memberships in mAccountTwo since it has no starred raw contacts.
8463         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8464         c = queryGroupMemberships(mAccount);
8465         try {
8466             assertTrue(c.moveToNext());
8467             assertEquals(g1, c.getLong(0));
8468             assertEquals(r1, c.getLong(1));
8469             assertFalse(c.moveToNext());
8470         } finally {
8471             c.close();
8472         }
8473 
8474         // remove the star from r1
8475         assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0"));
8476         // Since no raw contacts are starred, there should be no group memberships.
8477         assertNoRowsAndClose(queryGroupMemberships(mAccount));
8478         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8479 
8480         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(r1));
8481         assertNotNull(contactUri);
8482 
8483         // mark r1 as starred via its contact lookup uri
8484         assertEquals(1, updateItem(contactUri, Contacts.STARRED, "1"));
8485         // Now that r1 is starred it should have a membership in the one groups from mAccount
8486         // that is marked as a favorite.
8487         // There should be no memberships in mAccountTwo since it has no starred raw contacts.
8488         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8489         c = queryGroupMemberships(mAccount);
8490         try {
8491             assertTrue(c.moveToNext());
8492             assertEquals(g1, c.getLong(0));
8493             assertEquals(r1, c.getLong(1));
8494             assertFalse(c.moveToNext());
8495         } finally {
8496             c.close();
8497         }
8498 
8499         // remove the star from r1
8500         updateItem(contactUri, Contacts.STARRED, "0");
8501         // Since no raw contacts are starred, there should be no group memberships.
8502         assertNoRowsAndClose(queryGroupMemberships(mAccount));
8503         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8504     }
8505 
testStarChangedAfterGroupMembershipChange()8506     public void testStarChangedAfterGroupMembershipChange() {
8507         long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
8508         long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */);
8509         long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */);
8510         long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */);
8511         long r1 = RawContactUtil.createRawContact(mResolver, mAccount);
8512         long r2 = RawContactUtil.createRawContact(mResolver, mAccount);
8513         long r3 = RawContactUtil.createRawContact(mResolver, mAccountTwo);
8514 
8515         assertFalse(queryRawContactIsStarred(r1));
8516         assertFalse(queryRawContactIsStarred(r2));
8517         assertFalse(queryRawContactIsStarred(r3));
8518 
8519         Cursor c;
8520 
8521         // add r1 to one favorites group
8522         // r1's star should automatically be set
8523         // r1 should automatically be added to the other favorites group
8524         Uri urir1g1 = insertGroupMembership(r1, g1);
8525         assertTrue(queryRawContactIsStarred(r1));
8526         assertFalse(queryRawContactIsStarred(r2));
8527         assertFalse(queryRawContactIsStarred(r3));
8528         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8529         c = queryGroupMemberships(mAccount);
8530         try {
8531             assertTrue(c.moveToNext());
8532             assertEquals(g1, c.getLong(0));
8533             assertEquals(r1, c.getLong(1));
8534             assertFalse(c.moveToNext());
8535         } finally {
8536             c.close();
8537         }
8538 
8539         // remove r1 from one favorites group
8540         mResolver.delete(urir1g1, null, null);
8541         // r1's star should no longer be set
8542         assertFalse(queryRawContactIsStarred(r1));
8543         assertFalse(queryRawContactIsStarred(r2));
8544         assertFalse(queryRawContactIsStarred(r3));
8545         // there should be no membership rows
8546         assertNoRowsAndClose(queryGroupMemberships(mAccount));
8547         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8548 
8549         // add r3 to the one favorites group for that account
8550         // r3's star should automatically be set
8551         Uri urir3g4 = insertGroupMembership(r3, g4);
8552         assertFalse(queryRawContactIsStarred(r1));
8553         assertFalse(queryRawContactIsStarred(r2));
8554         assertTrue(queryRawContactIsStarred(r3));
8555         assertNoRowsAndClose(queryGroupMemberships(mAccount));
8556         c = queryGroupMemberships(mAccountTwo);
8557         try {
8558             assertTrue(c.moveToNext());
8559             assertEquals(g4, c.getLong(0));
8560             assertEquals(r3, c.getLong(1));
8561             assertFalse(c.moveToNext());
8562         } finally {
8563             c.close();
8564         }
8565 
8566         // remove r3 from the favorites group
8567         mResolver.delete(urir3g4, null, null);
8568         // r3's star should automatically be cleared
8569         assertFalse(queryRawContactIsStarred(r1));
8570         assertFalse(queryRawContactIsStarred(r2));
8571         assertFalse(queryRawContactIsStarred(r3));
8572         assertNoRowsAndClose(queryGroupMemberships(mAccount));
8573         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
8574     }
8575 
testReadOnlyRawContact()8576     public void testReadOnlyRawContact() {
8577         long rawContactId = RawContactUtil.createRawContact(mResolver);
8578         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
8579         storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first");
8580         storeValue(rawContactUri, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1);
8581 
8582         storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "second");
8583         assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first");
8584 
8585         Uri syncAdapterUri = rawContactUri.buildUpon()
8586                 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1")
8587                 .build();
8588         storeValue(syncAdapterUri, RawContacts.CUSTOM_RINGTONE, "third");
8589         assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "third");
8590     }
8591 
testReadOnlyDataRow()8592     public void testReadOnlyDataRow() {
8593         long rawContactId = RawContactUtil.createRawContact(mResolver);
8594         Uri emailUri = insertEmail(rawContactId, "email");
8595         Uri phoneUri = insertPhoneNumber(rawContactId, "555-1111");
8596 
8597         storeValue(emailUri, Data.IS_READ_ONLY, "1");
8598         storeValue(emailUri, Email.ADDRESS, "changed");
8599         storeValue(phoneUri, Phone.NUMBER, "555-2222");
8600         assertStoredValue(emailUri, Email.ADDRESS, "email");
8601         assertStoredValue(phoneUri, Phone.NUMBER, "555-2222");
8602 
8603         Uri syncAdapterUri = emailUri.buildUpon()
8604                 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1")
8605                 .build();
8606         storeValue(syncAdapterUri, Email.ADDRESS, "changed");
8607         assertStoredValue(emailUri, Email.ADDRESS, "changed");
8608     }
8609 
testContactWithReadOnlyRawContact()8610     public void testContactWithReadOnlyRawContact() {
8611         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
8612         Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
8613         storeValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "first");
8614 
8615         long rawContactId2 = RawContactUtil.createRawContact(mResolver);
8616         Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
8617         storeValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second");
8618         storeValue(rawContactUri2, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1);
8619 
8620         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
8621                 rawContactId1, rawContactId2);
8622 
8623         long contactId = queryContactId(rawContactId1);
8624 
8625         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
8626         storeValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt");
8627         assertStoredValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt");
8628         assertStoredValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "rt");
8629         assertStoredValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second");
8630     }
8631 
testNameParsingQuery()8632     public void testNameParsingQuery() {
8633         Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name")
8634                 .appendQueryParameter(StructuredName.DISPLAY_NAME, "Mr. John Q. Doe Jr.").build();
8635         Cursor cursor = mResolver.query(uri, null, null, null, null);
8636         ContentValues values = new ContentValues();
8637         values.put(StructuredName.DISPLAY_NAME, "Mr. John Q. Doe Jr.");
8638         values.put(StructuredName.PREFIX, "Mr.");
8639         values.put(StructuredName.GIVEN_NAME, "John");
8640         values.put(StructuredName.MIDDLE_NAME, "Q.");
8641         values.put(StructuredName.FAMILY_NAME, "Doe");
8642         values.put(StructuredName.SUFFIX, "Jr.");
8643         values.put(StructuredName.FULL_NAME_STYLE, FullNameStyle.WESTERN);
8644         assertTrue(cursor.moveToFirst());
8645         assertCursorValues(cursor, values);
8646         cursor.close();
8647     }
8648 
testNameConcatenationQuery()8649     public void testNameConcatenationQuery() {
8650         Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name")
8651                 .appendQueryParameter(StructuredName.PREFIX, "Mr")
8652                 .appendQueryParameter(StructuredName.GIVEN_NAME, "John")
8653                 .appendQueryParameter(StructuredName.MIDDLE_NAME, "Q.")
8654                 .appendQueryParameter(StructuredName.FAMILY_NAME, "Doe")
8655                 .appendQueryParameter(StructuredName.SUFFIX, "Jr.")
8656                 .build();
8657         Cursor cursor = mResolver.query(uri, null, null, null, null);
8658         ContentValues values = new ContentValues();
8659         values.put(StructuredName.DISPLAY_NAME, "Mr John Q. Doe, Jr.");
8660         values.put(StructuredName.PREFIX, "Mr");
8661         values.put(StructuredName.GIVEN_NAME, "John");
8662         values.put(StructuredName.MIDDLE_NAME, "Q.");
8663         values.put(StructuredName.FAMILY_NAME, "Doe");
8664         values.put(StructuredName.SUFFIX, "Jr.");
8665         values.put(StructuredName.FULL_NAME_STYLE, FullNameStyle.WESTERN);
8666         assertTrue(cursor.moveToFirst());
8667         assertCursorValues(cursor, values);
8668         cursor.close();
8669     }
8670 
testBuildSingleRowResult()8671     public void testBuildSingleRowResult() {
8672         checkBuildSingleRowResult(
8673                 new String[] {"b"},
8674                 new String[] {"a", "b"},
8675                 new Integer[] {1, 2},
8676                 new Integer[] {2}
8677                 );
8678 
8679         checkBuildSingleRowResult(
8680                 new String[] {"b", "a", "b"},
8681                 new String[] {"a", "b"},
8682                 new Integer[] {1, 2},
8683                 new Integer[] {2, 1, 2}
8684                 );
8685 
8686         checkBuildSingleRowResult(
8687                 null, // all columns
8688                 new String[] {"a", "b"},
8689                 new Integer[] {1, 2},
8690                 new Integer[] {1, 2}
8691                 );
8692 
8693         try {
8694             // Access non-existent column
8695             ContactsProvider2.buildSingleRowResult(new String[] {"a"}, new String[] {"b"},
8696                     new Object[] {1});
8697             fail();
8698         } catch (IllegalArgumentException expected) {
8699         }
8700     }
8701 
checkBuildSingleRowResult(String[] projection, String[] availableColumns, Object[] data, Integer[] expectedValues)8702     private void checkBuildSingleRowResult(String[] projection, String[] availableColumns,
8703             Object[] data, Integer[] expectedValues) {
8704         final Cursor c = ContactsProvider2.buildSingleRowResult(projection, availableColumns, data);
8705         try {
8706             assertTrue(c.moveToFirst());
8707             assertEquals(1, c.getCount());
8708             assertEquals(expectedValues.length, c.getColumnCount());
8709 
8710             for (int i = 0; i < expectedValues.length; i++) {
8711                 assertEquals("column " + i, expectedValues[i], (Integer) c.getInt(i));
8712             }
8713         } finally {
8714             c.close();
8715         }
8716     }
8717 
testDataUsageFeedbackAndDelete()8718     public void testDataUsageFeedbackAndDelete() {
8719 
8720         sMockClock.install();
8721         sMockClock.setCurrentTimeMillis(System.currentTimeMillis());
8722         final long startTime = sMockClock.currentTimeMillis();
8723 
8724         final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "contact", "a");
8725         final long did1a = ContentUris.parseId(insertEmail(rid1, "email_1_a@email.com"));
8726         final long did1b = ContentUris.parseId(insertEmail(rid1, "email_1_b@email.com"));
8727         final long did1p = ContentUris.parseId(insertPhoneNumber(rid1, "555-555-5555"));
8728 
8729         final long rid2 = RawContactUtil.createRawContactWithName(mResolver, "contact", "b");
8730         final long did2a = ContentUris.parseId(insertEmail(rid2, "email_2_a@email.com"));
8731         final long did2p = ContentUris.parseId(insertPhoneNumber(rid2, "555-555-5556"));
8732 
8733         // Aggregate 1 and 2
8734         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, rid1, rid2);
8735 
8736         final long rid3 = RawContactUtil.createRawContactWithName(mResolver, "contact", "c");
8737         final long did3a = ContentUris.parseId(insertEmail(rid3, "email_3@email.com"));
8738         final long did3p = ContentUris.parseId(insertPhoneNumber(rid3, "555-3333"));
8739 
8740         final long rid4 = RawContactUtil.createRawContactWithName(mResolver, "contact", "d");
8741         final long did4p = ContentUris.parseId(insertPhoneNumber(rid4, "555-4444"));
8742 
8743         final long cid1 = queryContactId(rid1);
8744         final long cid3 = queryContactId(rid3);
8745         final long cid4 = queryContactId(rid4);
8746 
8747         // Make sure 1+2, 3 and 4 aren't aggregated
8748         MoreAsserts.assertNotEqual(cid1, cid3);
8749         MoreAsserts.assertNotEqual(cid1, cid4);
8750         MoreAsserts.assertNotEqual(cid3, cid4);
8751 
8752         // time = startTime
8753 
8754         // First, there's no frequent.  (We use strequent here only because frequent is hidden
8755         // and may be removed someday.)
8756         assertRowCount(0, Contacts.CONTENT_STREQUENT_URI, null, null);
8757 
8758         // Test 1. touch data 1a
8759         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1a);
8760 
8761         // (We no longer populate frequent, so 0.)
8762         assertRowCount(0, Contacts.CONTENT_STREQUENT_URI, null, null);
8763 
8764         sMockClock.advanceDay();
8765 
8766         // Test 2. touch data 1a, 2a and 3a
8767         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1a, did2a, did3a);
8768 
8769         // (We no longer populate frequent, so 0.)
8770         assertRowCount(0, Contacts.CONTENT_STREQUENT_URI, null, null);
8771 
8772         sMockClock.advanceDay();
8773 
8774         // Test 2. touch data 2p (call)
8775         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did2p);
8776 
8777         // (We no longer populate frequent, so 0.)
8778         assertRowCount(0, Contacts.CONTENT_STREQUENT_URI, null, null);
8779 
8780         sMockClock.advanceDay();
8781 
8782         // Test 3. touch data 2p and 3p (short text)
8783         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, did2p, did3p);
8784 
8785         // Let's check the tables.
8786 
8787         // Fist, check the data_usage_stat table, which has no public URI.
8788         assertStoredValuesDb("SELECT " + DataUsageStatColumns.DATA_ID +
8789                 "," + DataUsageStatColumns.USAGE_TYPE_INT +
8790                 "," + DataUsageStatColumns.RAW_TIMES_USED +
8791                 "," + DataUsageStatColumns.RAW_LAST_TIME_USED +
8792                 " FROM " + Tables.DATA_USAGE_STAT, null
8793                 );
8794 
8795         // Next, check the raw_contacts table
8796         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
8797                 cv(RawContacts._ID, rid1,
8798                         RawContacts.TIMES_CONTACTED, 0,
8799                         RawContacts.LAST_TIME_CONTACTED, 0
8800                         ),
8801                 cv(RawContacts._ID, rid2,
8802                         RawContacts.TIMES_CONTACTED, 0,
8803                         RawContacts.LAST_TIME_CONTACTED, 0
8804                         ),
8805                 cv(RawContacts._ID, rid3,
8806                         RawContacts.TIMES_CONTACTED, 0,
8807                         RawContacts.LAST_TIME_CONTACTED, 0
8808                         ),
8809                 cv(RawContacts._ID, rid4,
8810                         RawContacts.TIMES_CONTACTED, 0,
8811                         RawContacts.LAST_TIME_CONTACTED, 0
8812                         )
8813                 );
8814 
8815         // Lastly, check the contacts table.
8816 
8817         // Note contact1.TIMES_CONTACTED = 4, even though raw_contact1.TIMES_CONTACTED +
8818         // raw_contact1.TIMES_CONTACTED = 5, because in test 2, data 1a and data 2a were touched
8819         // at once.
8820         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
8821                 cv(Contacts._ID, cid1,
8822                         Contacts.TIMES_CONTACTED, 0,
8823                         Contacts.LAST_TIME_CONTACTED, 0
8824                         ),
8825                 cv(Contacts._ID, cid3,
8826                         Contacts.TIMES_CONTACTED, 0,
8827                         Contacts.LAST_TIME_CONTACTED, 0
8828                         ),
8829                 cv(Contacts._ID, cid4,
8830                         Contacts.TIMES_CONTACTED, 0,
8831                         Contacts.LAST_TIME_CONTACTED, 0
8832                         )
8833                 );
8834 
8835         // Let's test the delete too.
8836         assertTrue(mResolver.delete(DataUsageFeedback.DELETE_USAGE_URI, null, null) > 0);
8837 
8838         // Now there's no frequent.
8839         assertRowCount(0, Contacts.CONTENT_STREQUENT_URI, null, null);
8840 
8841         // No rows in the stats table.
8842         assertStoredValuesDb("SELECT " + DataUsageStatColumns.DATA_ID +
8843                 " FROM " + Tables.DATA_USAGE_STAT, null,
8844                 new ContentValues[0]);
8845 
8846         // The following values should all be 0 or null.
8847         assertRowCount(0, Contacts.CONTENT_URI, Contacts.TIMES_CONTACTED + ">0", null);
8848         assertRowCount(0, Contacts.CONTENT_URI, Contacts.LAST_TIME_CONTACTED + ">0", null);
8849         assertRowCount(0, RawContacts.CONTENT_URI, RawContacts.TIMES_CONTACTED + ">0", null);
8850         assertRowCount(0, RawContacts.CONTENT_URI, RawContacts.LAST_TIME_CONTACTED + ">0", null);
8851 
8852         // Calling it when there's no usage stats will still return a positive value.
8853         assertTrue(mResolver.delete(DataUsageFeedback.DELETE_USAGE_URI, null, null) > 0);
8854     }
8855 
8856     /*******************************************************
8857      * Delta api tests.
8858      */
testContactDelete_hasDeleteLog()8859     public void testContactDelete_hasDeleteLog() {
8860         sMockClock.install();
8861         long start = sMockClock.currentTimeMillis();
8862         DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete();
8863         DatabaseAsserts.assertHasDeleteLogGreaterThan(mResolver, ids.mContactId, start);
8864 
8865         // Clean up. Must also remove raw contact.
8866         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
8867     }
8868 
testContactDelete_marksRawContactsForDeletion()8869     public void testContactDelete_marksRawContactsForDeletion() {
8870         DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(mAccount);
8871 
8872         String[] projection = new String[]{ContactsContract.RawContacts.DIRTY,
8873                 ContactsContract.RawContacts.DELETED};
8874         String[] record = RawContactUtil.queryByRawContactId(mResolver, ids.mRawContactId,
8875                 projection);
8876         assertEquals("1", record[0]);
8877         assertEquals("1", record[1]);
8878 
8879         // Clean up
8880         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
8881     }
8882 
testContactDelete_checkRawContactContactId()8883     public void testContactDelete_checkRawContactContactId() {
8884         DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(mAccount);
8885 
8886         String[] projection = new String[]{ContactsContract.RawContacts.CONTACT_ID};
8887         String[] record = RawContactUtil.queryByRawContactId(mResolver, ids.mRawContactId,
8888                 projection);
8889         assertNull(record[0]);
8890 
8891         // Clean up
8892         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
8893     }
8894 
testContactUpdate_metadataChange()8895     public void testContactUpdate_metadataChange() {
8896         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
8897         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, ids.mRawContactId);
8898         assertDirty(rawContactUri, true);
8899         clearDirty(rawContactUri);
8900 
8901         ContentValues values = new ContentValues();
8902         values.put(Contacts.PINNED, 1);
8903 
8904         ContactUtil.update(mResolver, ids.mContactId, values);
8905         assertDirty(rawContactUri, false);
8906         assertMetadataDirty(rawContactUri, false);
8907         assertNetworkNotified(false);
8908     }
8909 
testContactUpdate_updatesContactUpdatedTimestamp()8910     public void testContactUpdate_updatesContactUpdatedTimestamp() {
8911         sMockClock.install();
8912         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
8913 
8914         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
8915 
8916         ContentValues values = new ContentValues();
8917         values.put(ContactsContract.Contacts.STARRED, 1);
8918 
8919         sMockClock.advance();
8920         ContactUtil.update(mResolver, ids.mContactId, values);
8921 
8922         long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
8923         assertTrue(newTime > baseTime);
8924 
8925         // Clean up
8926         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
8927     }
8928 
8929     // This implicitly tests the Contact create case.
testRawContactCreate_updatesContactUpdatedTimestamp()8930     public void testRawContactCreate_updatesContactUpdatedTimestamp() {
8931         long startTime = System.currentTimeMillis();
8932 
8933         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
8934         long lastUpdated = getContactLastUpdatedTimestampByRawContactId(mResolver, rawContactId);
8935 
8936         assertTrue(lastUpdated > startTime);
8937 
8938         // Clean up
8939         RawContactUtil.delete(mResolver, rawContactId, true);
8940     }
8941 
testRawContactUpdate_updatesContactUpdatedTimestamp()8942     public void testRawContactUpdate_updatesContactUpdatedTimestamp() {
8943         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
8944 
8945         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
8946 
8947         ContentValues values = new ContentValues();
8948         values.put(ContactsContract.RawContacts.STARRED, 1);
8949         RawContactUtil.update(mResolver, ids.mRawContactId, values);
8950 
8951         long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
8952         assertTrue(newTime > baseTime);
8953 
8954         // Clean up
8955         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
8956     }
8957 
testRawContactPsuedoDelete_hasDeleteLogForContact()8958     public void testRawContactPsuedoDelete_hasDeleteLogForContact() {
8959         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
8960 
8961         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
8962 
8963         RawContactUtil.delete(mResolver, ids.mRawContactId, false);
8964 
8965         DatabaseAsserts.assertHasDeleteLogGreaterThan(mResolver, ids.mContactId, baseTime);
8966 
8967         // clean up
8968         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
8969     }
8970 
testRawContactDelete_hasDeleteLogForContact()8971     public void testRawContactDelete_hasDeleteLogForContact() {
8972         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
8973 
8974         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
8975 
8976         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
8977 
8978         DatabaseAsserts.assertHasDeleteLogGreaterThan(mResolver, ids.mContactId, baseTime);
8979 
8980         // already clean
8981     }
8982 
getContactLastUpdatedTimestampByRawContactId(ContentResolver resolver, long rawContactId)8983     private long getContactLastUpdatedTimestampByRawContactId(ContentResolver resolver,
8984             long rawContactId) {
8985         long contactId = RawContactUtil.queryContactIdByRawContactId(mResolver, rawContactId);
8986         MoreAsserts.assertNotEqual(CommonDatabaseUtils.NOT_FOUND, contactId);
8987 
8988         return ContactUtil.queryContactLastUpdatedTimestamp(mResolver, contactId);
8989     }
8990 
testDataInsert_updatesContactLastUpdatedTimestamp()8991     public void testDataInsert_updatesContactLastUpdatedTimestamp() {
8992         sMockClock.install();
8993         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
8994         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
8995 
8996         sMockClock.advance();
8997         insertPhoneNumberAndReturnDataId(ids.mRawContactId);
8998 
8999         long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
9000         assertTrue(newTime > baseTime);
9001 
9002         // Clean up
9003         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
9004     }
9005 
testDataDelete_updatesContactLastUpdatedTimestamp()9006     public void testDataDelete_updatesContactLastUpdatedTimestamp() {
9007         sMockClock.install();
9008         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
9009 
9010         long dataId = insertPhoneNumberAndReturnDataId(ids.mRawContactId);
9011 
9012         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
9013 
9014         sMockClock.advance();
9015         DataUtil.delete(mResolver, dataId);
9016 
9017         long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
9018         assertTrue(newTime > baseTime);
9019 
9020         // Clean up
9021         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
9022     }
9023 
testDataUpdate_updatesContactLastUpdatedTimestamp()9024     public void testDataUpdate_updatesContactLastUpdatedTimestamp() {
9025         sMockClock.install();
9026         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
9027 
9028         long dataId = insertPhoneNumberAndReturnDataId(ids.mRawContactId);
9029 
9030         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
9031 
9032         sMockClock.advance();
9033         ContentValues values = new ContentValues();
9034         values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, "555-5555");
9035         DataUtil.update(mResolver, dataId, values);
9036 
9037         long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
9038         assertTrue(newTime > baseTime);
9039 
9040         // Clean up
9041         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
9042     }
9043 
insertPhoneNumberAndReturnDataId(long rawContactId)9044     private long insertPhoneNumberAndReturnDataId(long rawContactId) {
9045         Uri uri = insertPhoneNumber(rawContactId, "1-800-GOOG-411");
9046         return ContentUris.parseId(uri);
9047     }
9048 
testDeletedContactsDelete_isUnsupported()9049     public void testDeletedContactsDelete_isUnsupported() {
9050         final Uri URI = ContactsContract.DeletedContacts.CONTENT_URI;
9051         DatabaseAsserts.assertDeleteIsUnsupported(mResolver, URI);
9052 
9053         Uri uri = ContentUris.withAppendedId(URI, 1L);
9054         DatabaseAsserts.assertDeleteIsUnsupported(mResolver, uri);
9055     }
9056 
testDeletedContactsInsert_isUnsupported()9057     public void testDeletedContactsInsert_isUnsupported() {
9058         final Uri URI = ContactsContract.DeletedContacts.CONTENT_URI;
9059         DatabaseAsserts.assertInsertIsUnsupported(mResolver, URI);
9060     }
9061 
9062 
testQueryDeletedContactsByContactId()9063     public void testQueryDeletedContactsByContactId() {
9064         DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete();
9065 
9066         MoreAsserts.assertNotEqual(CommonDatabaseUtils.NOT_FOUND,
9067                 DeletedContactUtil.queryDeletedTimestampForContactId(mResolver, ids.mContactId));
9068     }
9069 
testQueryDeletedContactsAll()9070     public void testQueryDeletedContactsAll() {
9071         final int numDeletes = 10;
9072 
9073         // Since we cannot clean out delete log from previous tests, we need to account for that
9074         // by querying for the count first.
9075         final long startCount = DeletedContactUtil.getCount(mResolver);
9076 
9077         for (int i = 0; i < numDeletes; i++) {
9078             assertContactCreateDelete();
9079         }
9080 
9081         final long endCount = DeletedContactUtil.getCount(mResolver);
9082 
9083         assertEquals(numDeletes, endCount - startCount);
9084     }
9085 
testQueryDeletedContactsSinceTimestamp()9086     public void testQueryDeletedContactsSinceTimestamp() {
9087         sMockClock.install();
9088 
9089         // Before
9090         final HashSet<Long> beforeIds = new HashSet<Long>();
9091         beforeIds.add(assertContactCreateDelete().mContactId);
9092         beforeIds.add(assertContactCreateDelete().mContactId);
9093 
9094         final long start = sMockClock.currentTimeMillis();
9095 
9096         // After
9097         final HashSet<Long> afterIds = new HashSet<Long>();
9098         afterIds.add(assertContactCreateDelete().mContactId);
9099         afterIds.add(assertContactCreateDelete().mContactId);
9100         afterIds.add(assertContactCreateDelete().mContactId);
9101 
9102         final String[] projection = new String[]{
9103                 ContactsContract.DeletedContacts.CONTACT_ID,
9104                 ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP
9105         };
9106         final List<String[]> records = DeletedContactUtil.querySinceTimestamp(mResolver, projection,
9107                 start);
9108         for (String[] record : records) {
9109             // Check ids to make sure we only have the ones that came after the time.
9110             final long contactId = Long.parseLong(record[0]);
9111             assertFalse(beforeIds.contains(contactId));
9112             assertTrue(afterIds.contains(contactId));
9113 
9114             // Check times to make sure they came after
9115             assertTrue(Long.parseLong(record[1]) > start);
9116         }
9117     }
9118 
9119     /**
9120      * Creates a contact in the local account. Assert it's not present in the delete log.
9121      * Delete it. And assert that the contact record is no longer present.
9122      *
9123      * @return The contact id and raw contact id that was created.
9124      */
assertContactCreateDelete()9125     private DatabaseAsserts.ContactIdPair assertContactCreateDelete() {
9126         return assertContactCreateDelete(null);
9127     }
9128 
9129     /**
9130      * Creates a contact in the given account. Assert it's not present in the delete log.
9131      * Delete it. And assert that the contact record is no longer present.
9132      * @return The contact id and raw contact id that was created.
9133      */
assertContactCreateDelete(Account account)9134     private DatabaseAsserts.ContactIdPair assertContactCreateDelete(Account account) {
9135         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver,
9136                 account);
9137 
9138         assertEquals(CommonDatabaseUtils.NOT_FOUND,
9139                 DeletedContactUtil.queryDeletedTimestampForContactId(mResolver, ids.mContactId));
9140 
9141         sMockClock.advance();
9142         ContactUtil.delete(mResolver, ids.mContactId);
9143 
9144         assertFalse(ContactUtil.recordExistsForContactId(mResolver, ids.mContactId));
9145 
9146         return ids;
9147     }
9148 
9149     /**
9150      * End delta api tests.
9151      ******************************************************/
9152 
9153     /*******************************************************
9154      * Pinning support tests
9155      */
testPinnedPositionsUpdate()9156     public void testPinnedPositionsUpdate() {
9157         final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
9158         final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
9159         final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
9160         final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
9161 
9162         final int unpinned = PinnedPositions.UNPINNED;
9163 
9164         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9165                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
9166                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
9167                 cv(Contacts._ID, i3.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
9168                 cv(Contacts._ID, i4.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0)
9169         );
9170 
9171         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
9172                 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, unpinned),
9173                 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned),
9174                 cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, unpinned),
9175                 cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, unpinned)
9176         );
9177 
9178         final ArrayList<ContentProviderOperation> operations =
9179                 new ArrayList<ContentProviderOperation>();
9180 
9181         operations.add(newPinningOperation(i1.mContactId, 1, true));
9182         operations.add(newPinningOperation(i3.mContactId, 3, true));
9183         operations.add(newPinningOperation(i4.mContactId, 2, false));
9184 
9185         CommonDatabaseUtils.applyBatch(mResolver, operations);
9186 
9187         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9188                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 1),
9189                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
9190                 cv(Contacts._ID, i3.mContactId, Contacts.PINNED, 3, Contacts.STARRED, 1),
9191                 cv(Contacts._ID, i4.mContactId, Contacts.PINNED, 2, Contacts.STARRED, 0)
9192         );
9193 
9194         // Make sure the values are propagated to raw contacts
9195 
9196         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
9197                 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1),
9198                 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned),
9199                 cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3),
9200                 cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, 2)
9201         );
9202 
9203         operations.clear();
9204 
9205         // Now unpin the contact
9206         operations.add(newPinningOperation(i3.mContactId, unpinned, false));
9207 
9208         CommonDatabaseUtils.applyBatch(mResolver, operations);
9209 
9210         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9211                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 1),
9212                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
9213                 cv(Contacts._ID, i3.mContactId, Contacts.PINNED, unpinned, Contacts.STARRED, 0),
9214                 cv(Contacts._ID, i4.mContactId, Contacts.PINNED, 2, Contacts.STARRED, 0)
9215         );
9216 
9217         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
9218                 cv(Contacts._ID, i1.mRawContactId, RawContacts.PINNED, 1, RawContacts.STARRED, 1),
9219                 cv(Contacts._ID, i2.mRawContactId, RawContacts.PINNED, unpinned,
9220                         RawContacts.STARRED, 0),
9221                 cv(Contacts._ID, i3.mRawContactId, RawContacts.PINNED, unpinned,
9222                         RawContacts.STARRED, 0),
9223                 cv(Contacts._ID, i4.mRawContactId, RawContacts.PINNED, 2, RawContacts.STARRED, 0)
9224         );
9225     }
9226 
testPinnedPositionsAfterJoinAndSplit()9227     public void testPinnedPositionsAfterJoinAndSplit() {
9228         final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContactWithName(
9229                 mResolver, "A", "Smith");
9230         final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContactWithName(
9231                 mResolver, "B", "Smith");
9232         final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContactWithName(
9233                 mResolver, "C", "Smith");
9234         final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContactWithName(
9235                 mResolver, "D", "Smith");
9236         final DatabaseAsserts.ContactIdPair i5 = DatabaseAsserts.assertAndCreateContactWithName(
9237                 mResolver, "E", "Smith");
9238         final DatabaseAsserts.ContactIdPair i6 = DatabaseAsserts.assertAndCreateContactWithName(
9239                 mResolver, "F", "Smith");
9240 
9241         final ArrayList<ContentProviderOperation> operations =
9242                 new ArrayList<ContentProviderOperation>();
9243 
9244         operations.add(newPinningOperation(i1.mContactId, 1, true));
9245         operations.add(newPinningOperation(i2.mContactId, 2, true));
9246         operations.add(newPinningOperation(i3.mContactId, 3, true));
9247         operations.add(newPinningOperation(i5.mContactId, 5, true));
9248         operations.add(newPinningOperation(i6.mContactId, 6, true));
9249 
9250         CommonDatabaseUtils.applyBatch(mResolver, operations);
9251 
9252         // aggregate raw contact 1 and 4 together.
9253         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, i1.mRawContactId,
9254                 i4.mRawContactId);
9255 
9256         // If only one contact is pinned, the resulting contact should inherit the pinned position
9257         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9258                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1),
9259                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 2),
9260                 cv(Contacts._ID, i3.mContactId, Contacts.PINNED, 3),
9261                 cv(Contacts._ID, i5.mContactId, Contacts.PINNED, 5),
9262                 cv(Contacts._ID, i6.mContactId, Contacts.PINNED, 6)
9263         );
9264 
9265         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
9266                 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1,
9267                         RawContacts.STARRED, 1),
9268                 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, 2,
9269                         RawContacts.STARRED, 1),
9270                 cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3,
9271                         RawContacts.STARRED, 1),
9272                 cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED,
9273                         RawContacts.STARRED, 0),
9274                 cv(RawContacts._ID, i5.mRawContactId, RawContacts.PINNED, 5,
9275                         RawContacts.STARRED, 1),
9276                 cv(RawContacts._ID, i6.mRawContactId, RawContacts.PINNED, 6,
9277                         RawContacts.STARRED, 1)
9278         );
9279 
9280         // aggregate raw contact 2 and 3 together.
9281         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, i2.mRawContactId,
9282                 i3.mRawContactId);
9283 
9284         // If both raw contacts are pinned, the resulting contact should inherit the lower
9285         // pinned position
9286         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9287                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1),
9288                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 2),
9289                 cv(Contacts._ID, i5.mContactId, Contacts.PINNED, 5),
9290                 cv(Contacts._ID, i6.mContactId, Contacts.PINNED, 6)
9291         );
9292 
9293         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
9294                 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1),
9295                 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, 2),
9296                 cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3),
9297                 cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED,
9298                         PinnedPositions.UNPINNED),
9299                 cv(RawContacts._ID, i5.mRawContactId, RawContacts.PINNED, 5),
9300                 cv(RawContacts._ID, i6.mRawContactId, RawContacts.PINNED, 6)
9301         );
9302 
9303         // split the aggregated raw contacts
9304         setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, i1.mRawContactId,
9305                 i4.mRawContactId);
9306 
9307         // raw contacts should be unpinned after being split, but still starred
9308         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
9309                 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1,
9310                         RawContacts.STARRED, 1),
9311                 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, 2,
9312                         RawContacts.STARRED, 1),
9313                 cv(RawContacts._ID, i3.mRawContactId, RawContacts.PINNED, 3,
9314                         RawContacts.STARRED, 1),
9315                 cv(RawContacts._ID, i4.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED,
9316                         RawContacts.STARRED, 0),
9317                 cv(RawContacts._ID, i5.mRawContactId, RawContacts.PINNED, 5,
9318                         RawContacts.STARRED, 1),
9319                 cv(RawContacts._ID, i6.mRawContactId, RawContacts.PINNED, 6,
9320                         RawContacts.STARRED, 1)
9321         );
9322 
9323         // now demote contact 5
9324         operations.clear();
9325         operations.add(newPinningOperation(i5.mContactId, PinnedPositions.DEMOTED, false));
9326         CommonDatabaseUtils.applyBatch(mResolver, operations);
9327 
9328         // Get new contact Ids for contacts composing of raw contacts 1 and 4 because they have
9329         // changed.
9330         final long cId1 = RawContactUtil.queryContactIdByRawContactId(mResolver, i1.mRawContactId);
9331         final long cId4 = RawContactUtil.queryContactIdByRawContactId(mResolver, i4.mRawContactId);
9332 
9333         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9334                 cv(Contacts._ID, cId1, Contacts.PINNED, 1),
9335                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 2),
9336                 cv(Contacts._ID, cId4, Contacts.PINNED, PinnedPositions.UNPINNED),
9337                 cv(Contacts._ID, i5.mContactId, Contacts.PINNED, PinnedPositions.DEMOTED),
9338                 cv(Contacts._ID, i6.mContactId, Contacts.PINNED, 6)
9339         );
9340 
9341         // aggregate contacts 5 and 6 together
9342         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, i5.mRawContactId,
9343                 i6.mRawContactId);
9344 
9345         // The resulting contact should have a pinned value of 6
9346         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9347                 cv(Contacts._ID, cId1, Contacts.PINNED, 1),
9348                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 2),
9349                 cv(Contacts._ID, cId4, Contacts.PINNED, PinnedPositions.UNPINNED),
9350                 cv(Contacts._ID, i5.mContactId, Contacts.PINNED, 6)
9351         );
9352     }
9353 
testDefaultAccountSet_throwException()9354     public void testDefaultAccountSet_throwException() {
9355         mActor.setAccounts(new Account[]{mAccount});
9356         try {
9357             mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD,
9358                     null, null);
9359             fail();
9360         } catch (SecurityException expected) {
9361         }
9362 
9363         mActor.addPermissions("android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS");
9364         try {
9365             Bundle bundle = new Bundle();
9366             bundle.putString(Settings.ACCOUNT_NAME, "account1"); // no account type specified
9367             mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD,
9368                     null, bundle);
9369             fail();
9370         } catch (IllegalArgumentException expected) {
9371         }
9372 
9373         try {
9374             Bundle bundle = new Bundle();
9375             bundle.putString(Settings.ACCOUNT_NAME, "account1");
9376             bundle.putString(Settings.ACCOUNT_TYPE, "account type1");
9377             bundle.putString(Settings.DATA_SET, "c"); // data set should not be set.
9378             mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD,
9379                     null, bundle);
9380             fail();
9381         } catch (IllegalArgumentException expected) {
9382         }
9383 
9384         try {
9385             Bundle bundle = new Bundle();
9386             bundle.putString(Settings.ACCOUNT_NAME, "account2"); // invalid account
9387             bundle.putString(Settings.ACCOUNT_TYPE, "account type2");
9388             mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD,
9389                 null, bundle);
9390             fail();
9391         } catch (IllegalArgumentException expected) {
9392         }
9393     }
9394 
testDefaultAccountSetAndQuery()9395     public void testDefaultAccountSetAndQuery() {
9396         Bundle response = mResolver.call(ContactsContract.AUTHORITY_URI,
9397                 Settings.QUERY_DEFAULT_ACCOUNT_METHOD, null, null);
9398         Account account = response.getParcelable(Settings.KEY_DEFAULT_ACCOUNT);
9399         assertNull(account);
9400 
9401         mActor.addPermissions("android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS");
9402         mActor.setAccounts(new Account[]{mAccount});
9403         // Set ("account1", "account type1") account as the default account.
9404         Bundle bundle = new Bundle();
9405         bundle.putString(Settings.ACCOUNT_NAME, "account1");
9406         bundle.putString(Settings.ACCOUNT_TYPE, "account type1");
9407         mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD,
9408                 null, bundle);
9409 
9410         response = mResolver.call(ContactsContract.AUTHORITY_URI,
9411                 Settings.QUERY_DEFAULT_ACCOUNT_METHOD, null, null);
9412         account = response.getParcelable(Settings.KEY_DEFAULT_ACCOUNT);
9413         assertEquals("account1", account.name);
9414         assertEquals("account type1", account.type);
9415 
9416         // Set NULL account as default account.
9417         bundle = new Bundle();
9418         mResolver.call(ContactsContract.AUTHORITY_URI, Settings.SET_DEFAULT_ACCOUNT_METHOD,
9419             null, bundle);
9420 
9421         response = mResolver.call(ContactsContract.AUTHORITY_URI,
9422                 Settings.QUERY_DEFAULT_ACCOUNT_METHOD, null, null);
9423         account = response.getParcelable(Settings.KEY_DEFAULT_ACCOUNT);
9424         assertNull(account);
9425     }
9426 
testPinnedPositionsDemoteIllegalArguments()9427     public void testPinnedPositionsDemoteIllegalArguments() {
9428         try {
9429             mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
9430                     null, null);
9431             fail();
9432         } catch (IllegalArgumentException expected) {
9433         }
9434 
9435         try {
9436             mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
9437                     "1.1", null);
9438             fail();
9439         } catch (IllegalArgumentException expected) {
9440         }
9441 
9442         try {
9443             mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
9444                     "NotANumber", null);
9445             fail();
9446         } catch (IllegalArgumentException expected) {
9447         }
9448 
9449         // Valid contact ID that does not correspond to an actual contact is silently ignored
9450         mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD, "999",
9451                 null);
9452     }
9453 
testPinnedPositionsAfterDemoteAndUndemote()9454     public void testPinnedPositionsAfterDemoteAndUndemote() {
9455         final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
9456         final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
9457 
9458         // Pin contact 1 and demote contact 2
9459         final ArrayList<ContentProviderOperation> operations =
9460                 new ArrayList<ContentProviderOperation>();
9461         operations.add(newPinningOperation(i1.mContactId, 1, true));
9462         operations.add(newPinningOperation(i2.mContactId, PinnedPositions.DEMOTED, false));
9463         CommonDatabaseUtils.applyBatch(mResolver, operations);
9464 
9465         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9466                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 1),
9467                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, PinnedPositions.DEMOTED,
9468                         Contacts.STARRED, 0)
9469         );
9470 
9471         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
9472                 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1,
9473                         RawContacts.STARRED, 1),
9474                 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, PinnedPositions.DEMOTED,
9475                         RawContacts.STARRED, 0)
9476         );
9477 
9478         // Now undemote both contacts
9479         mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
9480                 String.valueOf(i1.mContactId), null);
9481         mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
9482                 String.valueOf(i2.mContactId), null);
9483 
9484 
9485         // Contact 1 remains pinned at 0, while contact 2 becomes unpinned
9486         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9487                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1, Contacts.STARRED, 1),
9488                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED,
9489                         Contacts.STARRED, 0)
9490         );
9491 
9492         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
9493                 cv(RawContacts._ID, i1.mRawContactId, RawContacts.PINNED, 1,
9494                         RawContacts.STARRED, 1),
9495                 cv(RawContacts._ID, i2.mRawContactId, RawContacts.PINNED, PinnedPositions.UNPINNED,
9496                         RawContacts.STARRED, 0)
9497         );
9498     }
9499 
9500     /**
9501      * Verifies that any existing pinned contacts have their pinned positions incremented by one
9502      * after the upgrade step
9503      */
testPinnedPositionsUpgradeTo906_PinnedContactsIncrementedByOne()9504     public void testPinnedPositionsUpgradeTo906_PinnedContactsIncrementedByOne() {
9505         final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
9506         final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
9507         final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
9508         final ArrayList<ContentProviderOperation> operations =
9509                 new ArrayList<ContentProviderOperation>();
9510         operations.add(newPinningOperation(i1.mContactId, 0, true));
9511         operations.add(newPinningOperation(i2.mContactId, 5, true));
9512         operations.add(newPinningOperation(i3.mContactId, Integer.MAX_VALUE - 2, true));
9513         CommonDatabaseUtils.applyBatch(mResolver, operations);
9514 
9515         final ContactsDatabaseHelper helper =
9516                 ((ContactsDatabaseHelper) ((ContactsProvider2) getProvider()).getDatabaseHelper());
9517         SQLiteDatabase db = helper.getWritableDatabase();
9518         helper.upgradeToVersion906(db);
9519         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9520                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 1),
9521                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 6),
9522                 cv(Contacts._ID, i3.mContactId, Contacts.PINNED, Integer.MAX_VALUE - 1)
9523         );
9524     }
9525 
9526     /**
9527      * Verifies that any unpinned contacts (or those with pinned position Integer.MAX_VALUE - 1)
9528      * have their pinned positions correctly set to 0 after the upgrade step.
9529      */
testPinnedPositionsUpgradeTo906_UnpinnedValueCorrectlyUpdated()9530     public void testPinnedPositionsUpgradeTo906_UnpinnedValueCorrectlyUpdated() {
9531         final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
9532         final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
9533         final ArrayList<ContentProviderOperation> operations =
9534                 new ArrayList<ContentProviderOperation>();
9535         operations.add(newPinningOperation(i1.mContactId, Integer.MAX_VALUE -1 , true));
9536         operations.add(newPinningOperation(i2.mContactId, Integer.MAX_VALUE, true));
9537         CommonDatabaseUtils.applyBatch(mResolver, operations);
9538 
9539         final ContactsDatabaseHelper helper =
9540                 ((ContactsDatabaseHelper) ((ContactsProvider2) getProvider()).getDatabaseHelper());
9541         SQLiteDatabase db = helper.getWritableDatabase();
9542         helper.upgradeToVersion906(db);
9543 
9544         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9545                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 0),
9546                 cv(Contacts._ID, i2.mContactId, Contacts.PINNED, 0)
9547         );
9548     }
9549 
9550     /**
9551      * Tests the functionality of the
9552      * {@link ContactsContract.PinnedPositions#pin(ContentResolver, long, int)} API.
9553      */
testPinnedPositions_ContactsContractPinnedPositionsPin()9554     public void testPinnedPositions_ContactsContractPinnedPositionsPin() {
9555         final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
9556 
9557         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9558                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED)
9559         );
9560 
9561         ContactsContract.PinnedPositions.pin(mResolver, i1.mContactId, 5);
9562 
9563         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9564                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, 5)
9565         );
9566 
9567         ContactsContract.PinnedPositions.pin(mResolver, i1.mContactId, PinnedPositions.UNPINNED);
9568 
9569         assertStoredValuesWithProjection(Contacts.CONTENT_URI,
9570                 cv(Contacts._ID, i1.mContactId, Contacts.PINNED, PinnedPositions.UNPINNED)
9571         );
9572     }
9573 
newPinningOperation(long id, int pinned, boolean star)9574     private ContentProviderOperation newPinningOperation(long id, int pinned, boolean star) {
9575         final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_URI, String.valueOf(id));
9576         final ContentValues values = new ContentValues();
9577         values.put(Contacts.PINNED, pinned);
9578         values.put(Contacts.STARRED, star ? 1 : 0);
9579         return ContentProviderOperation.newUpdate(uri).withValues(values).build();
9580     }
9581 
9582     /**
9583      * End pinning support tests
9584      ******************************************************/
9585 
testAuthorization_authorize()9586     public void testAuthorization_authorize() throws Exception {
9587         // Setup
9588         ContentValues values = new ContentValues();
9589         long id1 = createContact(values, "Noah", "Tever", "18004664411",
9590                 "email@email.com", StatusUpdates.OFFLINE, 0, 0, 0, 0);
9591         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id1);
9592 
9593         // Execute: pre authorize the contact
9594         Uri authorizedUri = getPreAuthorizedUri(contactUri);
9595 
9596         // Sanity check: URIs are different
9597         assertNotSame(authorizedUri, contactUri);
9598 
9599         // Verify: the URI is pre authorized
9600         final ContactsProvider2 cp = (ContactsProvider2) getProvider();
9601         assertTrue(cp.isValidPreAuthorizedUri(authorizedUri));
9602     }
9603 
testAuthorization_unauthorized()9604     public void testAuthorization_unauthorized() throws Exception {
9605         // Setup
9606         ContentValues values = new ContentValues();
9607         long id1 = createContact(values, "Noah", "Tever", "18004664411",
9608                 "email@email.com", StatusUpdates.OFFLINE, 0, 0, 0, 0);
9609         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id1);
9610 
9611         // Verify: the URI is *not* pre authorized
9612         final ContactsProvider2 cp = (ContactsProvider2) getProvider();
9613         assertFalse(cp.isValidPreAuthorizedUri(contactUri));
9614     }
9615 
testAuthorization_invalidAuthorization()9616     public void testAuthorization_invalidAuthorization() throws Exception {
9617         // Setup
9618         ContentValues values = new ContentValues();
9619         long id1 = createContact(values, "Noah", "Tever", "18004664411",
9620                 "email@email.com", StatusUpdates.OFFLINE, 0, 0, 0, 0);
9621         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id1);
9622 
9623         // Execute: pre authorize the contact and then modify the resulting URI slightly
9624         Uri authorizedUri = getPreAuthorizedUri(contactUri);
9625         Uri almostAuthorizedUri = Uri.parse(authorizedUri.toString() + "2");
9626 
9627         // Verify: the URI is not pre authorized
9628         final ContactsProvider2 cp = (ContactsProvider2) getProvider();
9629         assertFalse(cp.isValidPreAuthorizedUri(almostAuthorizedUri));
9630     }
9631 
testAuthorization_expired()9632     public void testAuthorization_expired() throws Exception {
9633         // Setup
9634         ContentValues values = new ContentValues();
9635         long id1 = createContact(values, "Noah", "Tever", "18004664411",
9636                 "email@email.com", StatusUpdates.OFFLINE, 0, 0, 0, 0);
9637         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id1);
9638         sMockClock.install();
9639 
9640         // Execute: pre authorize the contact
9641         Uri authorizedUri = getPreAuthorizedUri(contactUri);
9642         sMockClock.setCurrentTimeMillis(sMockClock.currentTimeMillis() + 1000000);
9643 
9644         // Verify: the authorization for the URI expired
9645         final ContactsProvider2 cp = (ContactsProvider2) getProvider();
9646         assertFalse(cp.isValidPreAuthorizedUri(authorizedUri));
9647     }
9648 
testAuthorization_contactUpgrade()9649     public void testAuthorization_contactUpgrade() throws Exception {
9650         ContactsDatabaseHelper helper =
9651                 ((ContactsDatabaseHelper) ((ContactsProvider2) getProvider()).getDatabaseHelper());
9652         SQLiteDatabase db = helper.getWritableDatabase();
9653 
9654         // Perform the unit tests against an upgraded version of the database, instead of a freshly
9655         // created version of the database.
9656         helper.upgradeToVersion1002(db);
9657         testAuthorization_authorize();
9658         helper.upgradeToVersion1002(db);
9659         testAuthorization_expired();
9660         helper.upgradeToVersion1002(db);
9661         testAuthorization_expired();
9662         helper.upgradeToVersion1002(db);
9663         testAuthorization_invalidAuthorization();
9664     }
9665 
getPreAuthorizedUri(Uri uri)9666     private Uri getPreAuthorizedUri(Uri uri) {
9667         final Bundle uriBundle = new Bundle();
9668         uriBundle.putParcelable(ContactsContract.Authorization.KEY_URI_TO_AUTHORIZE, uri);
9669         final Bundle authResponse = mResolver.call(
9670                 ContactsContract.AUTHORITY_URI,
9671                 ContactsContract.Authorization.AUTHORIZATION_METHOD,
9672                 null,
9673                 uriBundle);
9674         return (Uri) authResponse.getParcelable(
9675                 ContactsContract.Authorization.KEY_AUTHORIZED_URI);
9676     }
9677 
9678     /**
9679      * End Authorization Tests
9680      ******************************************************/
9681 
queryGroupMemberships(Account account)9682     private Cursor queryGroupMemberships(Account account) {
9683         Cursor c = mResolver.query(TestUtil.maybeAddAccountQueryParameters(Data.CONTENT_URI,
9684                         account),
9685                 new String[] {GroupMembership.GROUP_ROW_ID, GroupMembership.RAW_CONTACT_ID},
9686                 Data.MIMETYPE + "=?", new String[] {GroupMembership.CONTENT_ITEM_TYPE},
9687                 GroupMembership.GROUP_SOURCE_ID);
9688         return c;
9689     }
9690 
readToEnd(FileInputStream inputStream)9691     private String readToEnd(FileInputStream inputStream) {
9692         try {
9693             System.out.println("DECLARED INPUT STREAM LENGTH: " + inputStream.available());
9694             int ch;
9695             StringBuilder stringBuilder = new StringBuilder();
9696             int index = 0;
9697             while (true) {
9698                 ch = inputStream.read();
9699                 System.out.println("READ CHARACTER: " + index + " " + ch);
9700                 if (ch == -1) {
9701                     break;
9702                 }
9703                 stringBuilder.append((char)ch);
9704                 index++;
9705             }
9706             return stringBuilder.toString();
9707         } catch (IOException e) {
9708             return null;
9709         }
9710     }
9711 
assertQueryParameter(String uriString, String parameter, String expectedValue)9712     private void assertQueryParameter(String uriString, String parameter, String expectedValue) {
9713         assertEquals(expectedValue, ContactsProvider2.getQueryParameter(
9714                 Uri.parse(uriString), parameter));
9715     }
9716 
createContact(ContentValues values, String firstName, String givenName, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode)9717     private long createContact(ContentValues values, String firstName, String givenName,
9718             String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
9719             long groupId, int chatMode) {
9720         return createContact(values, firstName, givenName, phoneNumber, email, presenceStatus,
9721                 timesContacted, starred, groupId, chatMode, false);
9722     }
9723 
createContact(ContentValues values, String firstName, String givenName, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode, boolean isUserProfile)9724     private long createContact(ContentValues values, String firstName, String givenName,
9725             String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
9726             long groupId, int chatMode, boolean isUserProfile) {
9727         return queryContactId(createRawContact(values, firstName, givenName, phoneNumber, email,
9728                 presenceStatus, timesContacted, starred, groupId, chatMode, isUserProfile));
9729     }
9730 
createRawContact(ContentValues values, String firstName, String givenName, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode)9731     private long createRawContact(ContentValues values, String firstName, String givenName,
9732             String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
9733             long groupId, int chatMode) {
9734         long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus,
9735                 timesContacted, starred, groupId, chatMode);
9736         DataUtil.insertStructuredName(mResolver, rawContactId, firstName, givenName);
9737         return rawContactId;
9738     }
9739 
createRawContact(ContentValues values, String firstName, String givenName, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode, boolean isUserProfile)9740     private long createRawContact(ContentValues values, String firstName, String givenName,
9741             String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
9742             long groupId, int chatMode, boolean isUserProfile) {
9743         long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus,
9744                 timesContacted, starred, groupId, chatMode, isUserProfile);
9745         DataUtil.insertStructuredName(mResolver, rawContactId, firstName, givenName);
9746         return rawContactId;
9747     }
9748 
createRawContact(ContentValues values, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode)9749     private long createRawContact(ContentValues values, String phoneNumber, String email,
9750             int presenceStatus, int timesContacted, int starred, long groupId, int chatMode) {
9751         return createRawContact(values, phoneNumber, email, presenceStatus, timesContacted, starred,
9752                 groupId, chatMode, false);
9753     }
9754 
createRawContact(ContentValues values, String phoneNumber, String email, int presenceStatus, int timesContacted, int starred, long groupId, int chatMode, boolean isUserProfile)9755     private long createRawContact(ContentValues values, String phoneNumber, String email,
9756             int presenceStatus, int timesContacted, int starred, long groupId, int chatMode,
9757             boolean isUserProfile) {
9758         values.put(RawContacts.STARRED, starred);
9759         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
9760         values.put(RawContacts.CUSTOM_RINGTONE, "beethoven5");
9761         values.put(RawContacts.TIMES_CONTACTED, timesContacted);
9762 
9763         Uri rawContactUri;
9764         if (isUserProfile) {
9765             rawContactUri = insertProfileRawContact(values);
9766         } else {
9767             rawContactUri = insertRawContact(values);
9768         }
9769 
9770         long rawContactId = ContentUris.parseId(rawContactUri);
9771         Uri photoUri = insertPhoto(rawContactId);
9772         long photoId = ContentUris.parseId(photoUri);
9773         values.put(Contacts.PHOTO_ID, photoId);
9774         if (!TextUtils.isEmpty(phoneNumber)) {
9775             insertPhoneNumber(rawContactId, phoneNumber);
9776         }
9777         if (!TextUtils.isEmpty(email)) {
9778             insertEmail(rawContactId, email);
9779         }
9780 
9781         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, email, presenceStatus, "hacking",
9782                 chatMode, isUserProfile);
9783 
9784         if (groupId != 0) {
9785             insertGroupMembership(rawContactId, groupId);
9786         }
9787 
9788         return rawContactId;
9789     }
9790 
9791     /**
9792      * Creates a raw contact with pre-set values under the user's profile.
9793      * @param profileValues Values to be used to create the entry (common values will be
9794      *     automatically populated in createRawContact()).
9795      * @return the raw contact ID that was created.
9796      */
createBasicProfileContact(ContentValues profileValues)9797     private long createBasicProfileContact(ContentValues profileValues) {
9798         long profileRawContactId = createRawContact(profileValues, "Mia", "Prophyl",
9799                 "18005554411", "mia.prophyl@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
9800                 StatusUpdates.CAPABILITY_HAS_CAMERA, true);
9801         profileValues.put(Contacts.DISPLAY_NAME, "Mia Prophyl");
9802         return profileRawContactId;
9803     }
9804 
9805     /**
9806      * Creates a raw contact with pre-set values that is not under the user's profile.
9807      * @param nonProfileValues Values to be used to create the entry (common values will be
9808      *     automatically populated in createRawContact()).
9809      * @return the raw contact ID that was created.
9810      */
createBasicNonProfileContact(ContentValues nonProfileValues)9811     private long createBasicNonProfileContact(ContentValues nonProfileValues) {
9812         long nonProfileRawContactId = createRawContact(nonProfileValues, "John", "Doe",
9813                 "18004664411", "goog411@acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
9814                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
9815         nonProfileValues.put(Contacts.DISPLAY_NAME, "John Doe");
9816         return nonProfileRawContactId;
9817     }
9818 
putDataValues(ContentValues values, long rawContactId)9819     private void putDataValues(ContentValues values, long rawContactId) {
9820         values.put(Data.RAW_CONTACT_ID, rawContactId);
9821         values.put(Data.MIMETYPE, "testmimetype");
9822         values.put(Data.RES_PACKAGE, "oldpackage");
9823         values.put(Data.IS_PRIMARY, 1);
9824         values.put(Data.IS_SUPER_PRIMARY, 1);
9825         values.put(Data.DATA1, "one");
9826         values.put(Data.DATA2, "two");
9827         values.put(Data.DATA3, "three");
9828         values.put(Data.DATA4, "four");
9829         values.put(Data.DATA5, "five");
9830         values.put(Data.DATA6, "six");
9831         values.put(Data.DATA7, "seven");
9832         values.put(Data.DATA8, "eight");
9833         values.put(Data.DATA9, "nine");
9834         values.put(Data.DATA10, "ten");
9835         values.put(Data.DATA11, "eleven");
9836         values.put(Data.DATA12, "twelve");
9837         values.put(Data.DATA13, "thirteen");
9838         values.put(Data.DATA14, "fourteen");
9839         values.put(Data.DATA15, "fifteen".getBytes());
9840         values.put(Data.CARRIER_PRESENCE, Data.CARRIER_PRESENCE_VT_CAPABLE);
9841         values.put(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME, "preferredcomponentname");
9842         values.put(Data.PREFERRED_PHONE_ACCOUNT_ID, "preferredid");
9843         values.put(Data.SYNC1, "sync1");
9844         values.put(Data.SYNC2, "sync2");
9845         values.put(Data.SYNC3, "sync3");
9846         values.put(Data.SYNC4, "sync4");
9847     }
9848 
9849     /**
9850      * @param data1 email address or phone number
9851      * @param usageType One of {@link DataUsageFeedback#USAGE_TYPE}
9852      * @param values ContentValues for this feedback. Useful for incrementing
9853      * {Contacts#TIMES_CONTACTED} in the ContentValue. Can be null.
9854      */
sendFeedback(String data1, String usageType, ContentValues values)9855     private void sendFeedback(String data1, String usageType, ContentValues values) {
9856         final long dataId = getStoredLongValue(Data.CONTENT_URI,
9857                 Data.DATA1 + "=?", new String[] { data1 }, Data._ID);
9858         assertEquals(0, updateDataUsageFeedback(usageType, dataId));
9859         if (values != null && values.containsKey(Contacts.TIMES_CONTACTED)) {
9860             values.put(Contacts.TIMES_CONTACTED, values.getAsInteger(Contacts.TIMES_CONTACTED) + 1);
9861         }
9862     }
9863 
updateDataUsageFeedback(String usageType, Uri resultUri)9864     private void updateDataUsageFeedback(String usageType, Uri resultUri) {
9865         final long id = ContentUris.parseId(resultUri);
9866         final boolean successful = updateDataUsageFeedback(usageType, id) > 0;
9867         assertFalse(successful); // shouldn't succeed
9868     }
9869 
updateDataUsageFeedback(String usageType, long... ids)9870     private int updateDataUsageFeedback(String usageType, long... ids) {
9871         final StringBuilder idList = new StringBuilder();
9872         for (long id : ids) {
9873             if (idList.length() > 0) idList.append(",");
9874             idList.append(id);
9875         }
9876         return mResolver.update(DataUsageFeedback.FEEDBACK_URI.buildUpon()
9877                 .appendPath(idList.toString())
9878                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, usageType)
9879                 .build(), new ContentValues(), null, null);
9880     }
9881 
hasChineseCollator()9882     private boolean hasChineseCollator() {
9883         final Locale locale[] = Collator.getAvailableLocales();
9884         for (int i = 0; i < locale.length; i++) {
9885             if (locale[i].equals(Locale.CHINA)) {
9886                 return true;
9887             }
9888         }
9889         return false;
9890     }
9891 
hasJapaneseCollator()9892     private boolean hasJapaneseCollator() {
9893         final Locale locale[] = Collator.getAvailableLocales();
9894         for (int i = 0; i < locale.length; i++) {
9895             if (locale[i].equals(Locale.JAPAN)) {
9896                 return true;
9897             }
9898         }
9899         return false;
9900     }
9901 
hasGermanCollator()9902     private boolean hasGermanCollator() {
9903         final Locale locale[] = Collator.getAvailableLocales();
9904         for (int i = 0; i < locale.length; i++) {
9905             if (locale[i].equals(Locale.GERMANY)) {
9906                 return true;
9907             }
9908         }
9909         return false;
9910     }
9911 
9912 
9913     /**
9914      * Asserts the equality of two Uri objects, ignoring the order of the query parameters.
9915      */
assertUriEquals(Uri expected, Uri actual)9916     public static void assertUriEquals(Uri expected, Uri actual) {
9917         assertEquals(expected.getScheme(), actual.getScheme());
9918         assertEquals(expected.getAuthority(), actual.getAuthority());
9919         assertEquals(expected.getPath(), actual.getPath());
9920         assertEquals(expected.getFragment(), actual.getFragment());
9921         Set<String> expectedParameterNames = expected.getQueryParameterNames();
9922         Set<String> actualParameterNames = actual.getQueryParameterNames();
9923         assertEquals(expectedParameterNames.size(), actualParameterNames.size());
9924         assertTrue(expectedParameterNames.containsAll(actualParameterNames));
9925         for (String parameterName : expectedParameterNames) {
9926             assertEquals(expected.getQueryParameter(parameterName),
9927                     actual.getQueryParameter(parameterName));
9928         }
9929 
9930     }
9931 }
9932