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