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