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