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