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 com.android.internal.util.ArrayUtils;
     20 import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
     21 import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
     22 import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
     23 import com.android.providers.contacts.tests.R;
     24 import com.google.android.collect.Lists;
     25 
     26 import android.accounts.Account;
     27 import android.content.ContentProviderOperation;
     28 import android.content.ContentProviderResult;
     29 import android.content.ContentUris;
     30 import android.content.ContentValues;
     31 import android.content.Entity;
     32 import android.content.EntityIterator;
     33 import android.content.res.AssetFileDescriptor;
     34 import android.database.Cursor;
     35 import android.net.Uri;
     36 import android.os.AsyncTask;
     37 import android.provider.ContactsContract;
     38 import android.provider.ContactsContract.AggregationExceptions;
     39 import android.provider.ContactsContract.CommonDataKinds.Email;
     40 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
     41 import android.provider.ContactsContract.CommonDataKinds.Im;
     42 import android.provider.ContactsContract.CommonDataKinds.Organization;
     43 import android.provider.ContactsContract.CommonDataKinds.Phone;
     44 import android.provider.ContactsContract.CommonDataKinds.Photo;
     45 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
     46 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
     47 import android.provider.ContactsContract.ContactCounts;
     48 import android.provider.ContactsContract.Contacts;
     49 import android.provider.ContactsContract.Data;
     50 import android.provider.ContactsContract.DataUsageFeedback;
     51 import android.provider.ContactsContract.Directory;
     52 import android.provider.ContactsContract.DisplayNameSources;
     53 import android.provider.ContactsContract.DisplayPhoto;
     54 import android.provider.ContactsContract.FullNameStyle;
     55 import android.provider.ContactsContract.Groups;
     56 import android.provider.ContactsContract.PhoneLookup;
     57 import android.provider.ContactsContract.PhoneticNameStyle;
     58 import android.provider.ContactsContract.Profile;
     59 import android.provider.ContactsContract.ProviderStatus;
     60 import android.provider.ContactsContract.RawContacts;
     61 import android.provider.ContactsContract.RawContactsEntity;
     62 import android.provider.ContactsContract.SearchSnippetColumns;
     63 import android.provider.ContactsContract.Settings;
     64 import android.provider.ContactsContract.StatusUpdates;
     65 import android.provider.ContactsContract.StreamItemPhotos;
     66 import android.provider.ContactsContract.StreamItems;
     67 import android.provider.OpenableColumns;
     68 import android.test.MoreAsserts;
     69 import android.test.suitebuilder.annotation.LargeTest;
     70 import android.text.TextUtils;
     71 
     72 import java.io.FileInputStream;
     73 import java.io.IOException;
     74 import java.io.InputStream;
     75 import java.io.OutputStream;
     76 import java.text.Collator;
     77 import java.util.ArrayList;
     78 import java.util.Arrays;
     79 import java.util.List;
     80 import java.util.Locale;
     81 
     82 /**
     83  * Unit tests for {@link ContactsProvider2}.
     84  *
     85  * Run the test like this:
     86  * <code>
     87    adb shell am instrument -e class com.android.providers.contacts.ContactsProvider2Test -w \
     88            com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
     89  * </code>
     90  */
     91 @LargeTest
     92 public class ContactsProvider2Test extends BaseContactsProvider2Test {
     93 
     94     private static final Account ACCOUNT_1 = new Account("account_name_1", "account_type_1");
     95     private static final Account ACCOUNT_2 = new Account("account_name_2", "account_type_2");
     96 
     97     public void testContactsProjection() {
     98         assertProjection(Contacts.CONTENT_URI, new String[]{
     99                 Contacts._ID,
    100                 Contacts.DISPLAY_NAME_PRIMARY,
    101                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    102                 Contacts.DISPLAY_NAME_SOURCE,
    103                 Contacts.PHONETIC_NAME,
    104                 Contacts.PHONETIC_NAME_STYLE,
    105                 Contacts.SORT_KEY_PRIMARY,
    106                 Contacts.SORT_KEY_ALTERNATIVE,
    107                 Contacts.LAST_TIME_CONTACTED,
    108                 Contacts.TIMES_CONTACTED,
    109                 Contacts.STARRED,
    110                 Contacts.IN_VISIBLE_GROUP,
    111                 Contacts.PHOTO_ID,
    112                 Contacts.PHOTO_FILE_ID,
    113                 Contacts.PHOTO_URI,
    114                 Contacts.PHOTO_THUMBNAIL_URI,
    115                 Contacts.CUSTOM_RINGTONE,
    116                 Contacts.HAS_PHONE_NUMBER,
    117                 Contacts.SEND_TO_VOICEMAIL,
    118                 Contacts.IS_USER_PROFILE,
    119                 Contacts.LOOKUP_KEY,
    120                 Contacts.NAME_RAW_CONTACT_ID,
    121                 Contacts.CONTACT_PRESENCE,
    122                 Contacts.CONTACT_CHAT_CAPABILITY,
    123                 Contacts.CONTACT_STATUS,
    124                 Contacts.CONTACT_STATUS_TIMESTAMP,
    125                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    126                 Contacts.CONTACT_STATUS_LABEL,
    127                 Contacts.CONTACT_STATUS_ICON,
    128         });
    129     }
    130 
    131     public void testContactsWithSnippetProjection() {
    132         assertProjection(Contacts.CONTENT_FILTER_URI.buildUpon().appendPath("nothing").build(),
    133             new String[]{
    134                 Contacts._ID,
    135                 Contacts.DISPLAY_NAME_PRIMARY,
    136                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    137                 Contacts.DISPLAY_NAME_SOURCE,
    138                 Contacts.PHONETIC_NAME,
    139                 Contacts.PHONETIC_NAME_STYLE,
    140                 Contacts.SORT_KEY_PRIMARY,
    141                 Contacts.SORT_KEY_ALTERNATIVE,
    142                 Contacts.LAST_TIME_CONTACTED,
    143                 Contacts.TIMES_CONTACTED,
    144                 Contacts.STARRED,
    145                 Contacts.IN_VISIBLE_GROUP,
    146                 Contacts.PHOTO_ID,
    147                 Contacts.PHOTO_FILE_ID,
    148                 Contacts.PHOTO_URI,
    149                 Contacts.PHOTO_THUMBNAIL_URI,
    150                 Contacts.CUSTOM_RINGTONE,
    151                 Contacts.HAS_PHONE_NUMBER,
    152                 Contacts.SEND_TO_VOICEMAIL,
    153                 Contacts.IS_USER_PROFILE,
    154                 Contacts.LOOKUP_KEY,
    155                 Contacts.NAME_RAW_CONTACT_ID,
    156                 Contacts.CONTACT_PRESENCE,
    157                 Contacts.CONTACT_CHAT_CAPABILITY,
    158                 Contacts.CONTACT_STATUS,
    159                 Contacts.CONTACT_STATUS_TIMESTAMP,
    160                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    161                 Contacts.CONTACT_STATUS_LABEL,
    162                 Contacts.CONTACT_STATUS_ICON,
    163 
    164                 SearchSnippetColumns.SNIPPET,
    165         });
    166     }
    167 
    168     public void testRawContactsProjection() {
    169         assertProjection(RawContacts.CONTENT_URI, new String[]{
    170                 RawContacts._ID,
    171                 RawContacts.CONTACT_ID,
    172                 RawContacts.ACCOUNT_NAME,
    173                 RawContacts.ACCOUNT_TYPE,
    174                 RawContacts.DATA_SET,
    175                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
    176                 RawContacts.SOURCE_ID,
    177                 RawContacts.VERSION,
    178                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
    179                 RawContacts.DIRTY,
    180                 RawContacts.DELETED,
    181                 RawContacts.DISPLAY_NAME_PRIMARY,
    182                 RawContacts.DISPLAY_NAME_ALTERNATIVE,
    183                 RawContacts.DISPLAY_NAME_SOURCE,
    184                 RawContacts.PHONETIC_NAME,
    185                 RawContacts.PHONETIC_NAME_STYLE,
    186                 RawContacts.NAME_VERIFIED,
    187                 RawContacts.SORT_KEY_PRIMARY,
    188                 RawContacts.SORT_KEY_ALTERNATIVE,
    189                 RawContacts.TIMES_CONTACTED,
    190                 RawContacts.LAST_TIME_CONTACTED,
    191                 RawContacts.CUSTOM_RINGTONE,
    192                 RawContacts.SEND_TO_VOICEMAIL,
    193                 RawContacts.STARRED,
    194                 RawContacts.AGGREGATION_MODE,
    195                 RawContacts.SYNC1,
    196                 RawContacts.SYNC2,
    197                 RawContacts.SYNC3,
    198                 RawContacts.SYNC4,
    199         });
    200     }
    201 
    202     public void testDataProjection() {
    203         assertProjection(Data.CONTENT_URI, new String[]{
    204                 Data._ID,
    205                 Data.RAW_CONTACT_ID,
    206                 Data.DATA_VERSION,
    207                 Data.IS_PRIMARY,
    208                 Data.IS_SUPER_PRIMARY,
    209                 Data.RES_PACKAGE,
    210                 Data.MIMETYPE,
    211                 Data.DATA1,
    212                 Data.DATA2,
    213                 Data.DATA3,
    214                 Data.DATA4,
    215                 Data.DATA5,
    216                 Data.DATA6,
    217                 Data.DATA7,
    218                 Data.DATA8,
    219                 Data.DATA9,
    220                 Data.DATA10,
    221                 Data.DATA11,
    222                 Data.DATA12,
    223                 Data.DATA13,
    224                 Data.DATA14,
    225                 Data.DATA15,
    226                 Data.SYNC1,
    227                 Data.SYNC2,
    228                 Data.SYNC3,
    229                 Data.SYNC4,
    230                 Data.CONTACT_ID,
    231                 Data.PRESENCE,
    232                 Data.CHAT_CAPABILITY,
    233                 Data.STATUS,
    234                 Data.STATUS_TIMESTAMP,
    235                 Data.STATUS_RES_PACKAGE,
    236                 Data.STATUS_LABEL,
    237                 Data.STATUS_ICON,
    238                 RawContacts.ACCOUNT_NAME,
    239                 RawContacts.ACCOUNT_TYPE,
    240                 RawContacts.DATA_SET,
    241                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
    242                 RawContacts.SOURCE_ID,
    243                 RawContacts.VERSION,
    244                 RawContacts.DIRTY,
    245                 RawContacts.NAME_VERIFIED,
    246                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
    247                 Contacts._ID,
    248                 Contacts.DISPLAY_NAME_PRIMARY,
    249                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    250                 Contacts.DISPLAY_NAME_SOURCE,
    251                 Contacts.PHONETIC_NAME,
    252                 Contacts.PHONETIC_NAME_STYLE,
    253                 Contacts.SORT_KEY_PRIMARY,
    254                 Contacts.SORT_KEY_ALTERNATIVE,
    255                 Contacts.LAST_TIME_CONTACTED,
    256                 Contacts.TIMES_CONTACTED,
    257                 Contacts.STARRED,
    258                 Contacts.IN_VISIBLE_GROUP,
    259                 Contacts.PHOTO_ID,
    260                 Contacts.PHOTO_FILE_ID,
    261                 Contacts.PHOTO_URI,
    262                 Contacts.PHOTO_THUMBNAIL_URI,
    263                 Contacts.CUSTOM_RINGTONE,
    264                 Contacts.SEND_TO_VOICEMAIL,
    265                 Contacts.LOOKUP_KEY,
    266                 Contacts.NAME_RAW_CONTACT_ID,
    267                 Contacts.HAS_PHONE_NUMBER,
    268                 Contacts.CONTACT_PRESENCE,
    269                 Contacts.CONTACT_CHAT_CAPABILITY,
    270                 Contacts.CONTACT_STATUS,
    271                 Contacts.CONTACT_STATUS_TIMESTAMP,
    272                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    273                 Contacts.CONTACT_STATUS_LABEL,
    274                 Contacts.CONTACT_STATUS_ICON,
    275                 GroupMembership.GROUP_SOURCE_ID,
    276         });
    277     }
    278 
    279     public void testDistinctDataProjection() {
    280         assertProjection(Phone.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(),
    281             new String[]{
    282                 Data._ID,
    283                 Data.DATA_VERSION,
    284                 Data.IS_PRIMARY,
    285                 Data.IS_SUPER_PRIMARY,
    286                 Data.RES_PACKAGE,
    287                 Data.MIMETYPE,
    288                 Data.DATA1,
    289                 Data.DATA2,
    290                 Data.DATA3,
    291                 Data.DATA4,
    292                 Data.DATA5,
    293                 Data.DATA6,
    294                 Data.DATA7,
    295                 Data.DATA8,
    296                 Data.DATA9,
    297                 Data.DATA10,
    298                 Data.DATA11,
    299                 Data.DATA12,
    300                 Data.DATA13,
    301                 Data.DATA14,
    302                 Data.DATA15,
    303                 Data.SYNC1,
    304                 Data.SYNC2,
    305                 Data.SYNC3,
    306                 Data.SYNC4,
    307                 Data.CONTACT_ID,
    308                 Data.PRESENCE,
    309                 Data.CHAT_CAPABILITY,
    310                 Data.STATUS,
    311                 Data.STATUS_TIMESTAMP,
    312                 Data.STATUS_RES_PACKAGE,
    313                 Data.STATUS_LABEL,
    314                 Data.STATUS_ICON,
    315                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
    316                 Contacts._ID,
    317                 Contacts.DISPLAY_NAME_PRIMARY,
    318                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    319                 Contacts.DISPLAY_NAME_SOURCE,
    320                 Contacts.PHONETIC_NAME,
    321                 Contacts.PHONETIC_NAME_STYLE,
    322                 Contacts.SORT_KEY_PRIMARY,
    323                 Contacts.SORT_KEY_ALTERNATIVE,
    324                 Contacts.LAST_TIME_CONTACTED,
    325                 Contacts.TIMES_CONTACTED,
    326                 Contacts.STARRED,
    327                 Contacts.IN_VISIBLE_GROUP,
    328                 Contacts.PHOTO_ID,
    329                 Contacts.PHOTO_FILE_ID,
    330                 Contacts.PHOTO_URI,
    331                 Contacts.PHOTO_THUMBNAIL_URI,
    332                 Contacts.HAS_PHONE_NUMBER,
    333                 Contacts.CUSTOM_RINGTONE,
    334                 Contacts.SEND_TO_VOICEMAIL,
    335                 Contacts.LOOKUP_KEY,
    336                 Contacts.CONTACT_PRESENCE,
    337                 Contacts.CONTACT_CHAT_CAPABILITY,
    338                 Contacts.CONTACT_STATUS,
    339                 Contacts.CONTACT_STATUS_TIMESTAMP,
    340                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    341                 Contacts.CONTACT_STATUS_LABEL,
    342                 Contacts.CONTACT_STATUS_ICON,
    343                 GroupMembership.GROUP_SOURCE_ID,
    344         });
    345     }
    346 
    347     public void testEntityProjection() {
    348         assertProjection(
    349             Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, 0),
    350                     Contacts.Entity.CONTENT_DIRECTORY),
    351             new String[]{
    352                 Contacts.Entity._ID,
    353                 Contacts.Entity.DATA_ID,
    354                 Contacts.Entity.RAW_CONTACT_ID,
    355                 Data.DATA_VERSION,
    356                 Data.IS_PRIMARY,
    357                 Data.IS_SUPER_PRIMARY,
    358                 Data.RES_PACKAGE,
    359                 Data.MIMETYPE,
    360                 Data.DATA1,
    361                 Data.DATA2,
    362                 Data.DATA3,
    363                 Data.DATA4,
    364                 Data.DATA5,
    365                 Data.DATA6,
    366                 Data.DATA7,
    367                 Data.DATA8,
    368                 Data.DATA9,
    369                 Data.DATA10,
    370                 Data.DATA11,
    371                 Data.DATA12,
    372                 Data.DATA13,
    373                 Data.DATA14,
    374                 Data.DATA15,
    375                 Data.SYNC1,
    376                 Data.SYNC2,
    377                 Data.SYNC3,
    378                 Data.SYNC4,
    379                 Data.CONTACT_ID,
    380                 Data.PRESENCE,
    381                 Data.CHAT_CAPABILITY,
    382                 Data.STATUS,
    383                 Data.STATUS_TIMESTAMP,
    384                 Data.STATUS_RES_PACKAGE,
    385                 Data.STATUS_LABEL,
    386                 Data.STATUS_ICON,
    387                 RawContacts.ACCOUNT_NAME,
    388                 RawContacts.ACCOUNT_TYPE,
    389                 RawContacts.DATA_SET,
    390                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
    391                 RawContacts.SOURCE_ID,
    392                 RawContacts.VERSION,
    393                 RawContacts.DELETED,
    394                 RawContacts.DIRTY,
    395                 RawContacts.NAME_VERIFIED,
    396                 RawContacts.SYNC1,
    397                 RawContacts.SYNC2,
    398                 RawContacts.SYNC3,
    399                 RawContacts.SYNC4,
    400                 Contacts._ID,
    401                 Contacts.DISPLAY_NAME_PRIMARY,
    402                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    403                 Contacts.DISPLAY_NAME_SOURCE,
    404                 Contacts.PHONETIC_NAME,
    405                 Contacts.PHONETIC_NAME_STYLE,
    406                 Contacts.SORT_KEY_PRIMARY,
    407                 Contacts.SORT_KEY_ALTERNATIVE,
    408                 Contacts.LAST_TIME_CONTACTED,
    409                 Contacts.TIMES_CONTACTED,
    410                 Contacts.STARRED,
    411                 Contacts.IN_VISIBLE_GROUP,
    412                 Contacts.PHOTO_ID,
    413                 Contacts.PHOTO_FILE_ID,
    414                 Contacts.PHOTO_URI,
    415                 Contacts.PHOTO_THUMBNAIL_URI,
    416                 Contacts.CUSTOM_RINGTONE,
    417                 Contacts.SEND_TO_VOICEMAIL,
    418                 Contacts.IS_USER_PROFILE,
    419                 Contacts.LOOKUP_KEY,
    420                 Contacts.NAME_RAW_CONTACT_ID,
    421                 Contacts.HAS_PHONE_NUMBER,
    422                 Contacts.CONTACT_PRESENCE,
    423                 Contacts.CONTACT_CHAT_CAPABILITY,
    424                 Contacts.CONTACT_STATUS,
    425                 Contacts.CONTACT_STATUS_TIMESTAMP,
    426                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    427                 Contacts.CONTACT_STATUS_LABEL,
    428                 Contacts.CONTACT_STATUS_ICON,
    429                 GroupMembership.GROUP_SOURCE_ID,
    430         });
    431     }
    432 
    433     public void testRawEntityProjection() {
    434         assertProjection(RawContactsEntity.CONTENT_URI, new String[]{
    435                 RawContacts.Entity.DATA_ID,
    436                 RawContacts._ID,
    437                 RawContacts.CONTACT_ID,
    438                 RawContacts.ACCOUNT_NAME,
    439                 RawContacts.ACCOUNT_TYPE,
    440                 RawContacts.DATA_SET,
    441                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
    442                 RawContacts.SOURCE_ID,
    443                 RawContacts.VERSION,
    444                 RawContacts.DIRTY,
    445                 RawContacts.NAME_VERIFIED,
    446                 RawContacts.DELETED,
    447                 RawContacts.SYNC1,
    448                 RawContacts.SYNC2,
    449                 RawContacts.SYNC3,
    450                 RawContacts.SYNC4,
    451                 RawContacts.STARRED,
    452                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
    453                 Data.DATA_VERSION,
    454                 Data.IS_PRIMARY,
    455                 Data.IS_SUPER_PRIMARY,
    456                 Data.RES_PACKAGE,
    457                 Data.MIMETYPE,
    458                 Data.DATA1,
    459                 Data.DATA2,
    460                 Data.DATA3,
    461                 Data.DATA4,
    462                 Data.DATA5,
    463                 Data.DATA6,
    464                 Data.DATA7,
    465                 Data.DATA8,
    466                 Data.DATA9,
    467                 Data.DATA10,
    468                 Data.DATA11,
    469                 Data.DATA12,
    470                 Data.DATA13,
    471                 Data.DATA14,
    472                 Data.DATA15,
    473                 Data.SYNC1,
    474                 Data.SYNC2,
    475                 Data.SYNC3,
    476                 Data.SYNC4,
    477                 GroupMembership.GROUP_SOURCE_ID,
    478         });
    479     }
    480 
    481     public void testPhoneLookupProjection() {
    482         assertProjection(PhoneLookup.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(),
    483             new String[]{
    484                 PhoneLookup._ID,
    485                 PhoneLookup.LOOKUP_KEY,
    486                 PhoneLookup.DISPLAY_NAME,
    487                 PhoneLookup.LAST_TIME_CONTACTED,
    488                 PhoneLookup.TIMES_CONTACTED,
    489                 PhoneLookup.STARRED,
    490                 PhoneLookup.IN_VISIBLE_GROUP,
    491                 PhoneLookup.PHOTO_ID,
    492                 PhoneLookup.PHOTO_URI,
    493                 PhoneLookup.PHOTO_THUMBNAIL_URI,
    494                 PhoneLookup.CUSTOM_RINGTONE,
    495                 PhoneLookup.HAS_PHONE_NUMBER,
    496                 PhoneLookup.SEND_TO_VOICEMAIL,
    497                 PhoneLookup.NUMBER,
    498                 PhoneLookup.TYPE,
    499                 PhoneLookup.LABEL,
    500                 PhoneLookup.NORMALIZED_NUMBER,
    501         });
    502     }
    503 
    504     public void testGroupsProjection() {
    505         assertProjection(Groups.CONTENT_URI, new String[]{
    506                 Groups._ID,
    507                 Groups.ACCOUNT_NAME,
    508                 Groups.ACCOUNT_TYPE,
    509                 Groups.DATA_SET,
    510                 Groups.ACCOUNT_TYPE_AND_DATA_SET,
    511                 Groups.SOURCE_ID,
    512                 Groups.DIRTY,
    513                 Groups.VERSION,
    514                 Groups.RES_PACKAGE,
    515                 Groups.TITLE,
    516                 Groups.TITLE_RES,
    517                 Groups.GROUP_VISIBLE,
    518                 Groups.SYSTEM_ID,
    519                 Groups.DELETED,
    520                 Groups.NOTES,
    521                 Groups.SHOULD_SYNC,
    522                 Groups.FAVORITES,
    523                 Groups.AUTO_ADD,
    524                 Groups.GROUP_IS_READ_ONLY,
    525                 Groups.SYNC1,
    526                 Groups.SYNC2,
    527                 Groups.SYNC3,
    528                 Groups.SYNC4,
    529         });
    530     }
    531 
    532     public void testGroupsSummaryProjection() {
    533         assertProjection(Groups.CONTENT_SUMMARY_URI, new String[]{
    534                 Groups._ID,
    535                 Groups.ACCOUNT_NAME,
    536                 Groups.ACCOUNT_TYPE,
    537                 Groups.DATA_SET,
    538                 Groups.ACCOUNT_TYPE_AND_DATA_SET,
    539                 Groups.SOURCE_ID,
    540                 Groups.DIRTY,
    541                 Groups.VERSION,
    542                 Groups.RES_PACKAGE,
    543                 Groups.TITLE,
    544                 Groups.TITLE_RES,
    545                 Groups.GROUP_VISIBLE,
    546                 Groups.SYSTEM_ID,
    547                 Groups.DELETED,
    548                 Groups.NOTES,
    549                 Groups.SHOULD_SYNC,
    550                 Groups.FAVORITES,
    551                 Groups.AUTO_ADD,
    552                 Groups.GROUP_IS_READ_ONLY,
    553                 Groups.SYNC1,
    554                 Groups.SYNC2,
    555                 Groups.SYNC3,
    556                 Groups.SYNC4,
    557                 Groups.SUMMARY_COUNT,
    558                 Groups.SUMMARY_WITH_PHONES,
    559         });
    560     }
    561 
    562     public void testAggregateExceptionProjection() {
    563         assertProjection(AggregationExceptions.CONTENT_URI, new String[]{
    564                 AggregationExceptionColumns._ID,
    565                 AggregationExceptions.TYPE,
    566                 AggregationExceptions.RAW_CONTACT_ID1,
    567                 AggregationExceptions.RAW_CONTACT_ID2,
    568         });
    569     }
    570 
    571     public void testSettingsProjection() {
    572         assertProjection(Settings.CONTENT_URI, new String[]{
    573                 Settings.ACCOUNT_NAME,
    574                 Settings.ACCOUNT_TYPE,
    575                 Settings.DATA_SET,
    576                 Settings.UNGROUPED_VISIBLE,
    577                 Settings.SHOULD_SYNC,
    578                 Settings.ANY_UNSYNCED,
    579                 Settings.UNGROUPED_COUNT,
    580                 Settings.UNGROUPED_WITH_PHONES,
    581         });
    582     }
    583 
    584     public void testStatusUpdatesProjection() {
    585         assertProjection(StatusUpdates.CONTENT_URI, new String[]{
    586                 PresenceColumns.RAW_CONTACT_ID,
    587                 StatusUpdates.DATA_ID,
    588                 StatusUpdates.IM_ACCOUNT,
    589                 StatusUpdates.IM_HANDLE,
    590                 StatusUpdates.PROTOCOL,
    591                 StatusUpdates.CUSTOM_PROTOCOL,
    592                 StatusUpdates.PRESENCE,
    593                 StatusUpdates.CHAT_CAPABILITY,
    594                 StatusUpdates.STATUS,
    595                 StatusUpdates.STATUS_TIMESTAMP,
    596                 StatusUpdates.STATUS_RES_PACKAGE,
    597                 StatusUpdates.STATUS_ICON,
    598                 StatusUpdates.STATUS_LABEL,
    599         });
    600     }
    601 
    602     public void testDirectoryProjection() {
    603         assertProjection(Directory.CONTENT_URI, new String[]{
    604                 Directory._ID,
    605                 Directory.PACKAGE_NAME,
    606                 Directory.TYPE_RESOURCE_ID,
    607                 Directory.DISPLAY_NAME,
    608                 Directory.DIRECTORY_AUTHORITY,
    609                 Directory.ACCOUNT_TYPE,
    610                 Directory.ACCOUNT_NAME,
    611                 Directory.EXPORT_SUPPORT,
    612                 Directory.SHORTCUT_SUPPORT,
    613                 Directory.PHOTO_SUPPORT,
    614         });
    615     }
    616 
    617     public void testRawContactsInsert() {
    618         ContentValues values = new ContentValues();
    619 
    620         values.put(RawContacts.ACCOUNT_NAME, "a");
    621         values.put(RawContacts.ACCOUNT_TYPE, "b");
    622         values.put(RawContacts.SOURCE_ID, "c");
    623         values.put(RawContacts.VERSION, 42);
    624         values.put(RawContacts.DIRTY, 1);
    625         values.put(RawContacts.DELETED, 1);
    626         values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
    627         values.put(RawContacts.CUSTOM_RINGTONE, "d");
    628         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
    629         values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
    630         values.put(RawContacts.STARRED, 1);
    631         values.put(RawContacts.SYNC1, "e");
    632         values.put(RawContacts.SYNC2, "f");
    633         values.put(RawContacts.SYNC3, "g");
    634         values.put(RawContacts.SYNC4, "h");
    635 
    636         Uri rowUri = mResolver.insert(RawContacts.CONTENT_URI, values);
    637         long rawContactId = ContentUris.parseId(rowUri);
    638 
    639         assertStoredValues(rowUri, values);
    640         assertSelection(RawContacts.CONTENT_URI, values, RawContacts._ID, rawContactId);
    641         assertNetworkNotified(true);
    642     }
    643 
    644     public void testDataDirectoryWithLookupUri() {
    645         ContentValues values = new ContentValues();
    646 
    647         long rawContactId = createRawContactWithName();
    648         insertPhoneNumber(rawContactId, "555-GOOG-411");
    649         insertEmail(rawContactId, "google (at) android.com");
    650 
    651         long contactId = queryContactId(rawContactId);
    652         String lookupKey = queryLookupKey(contactId);
    653 
    654         // Complete and valid lookup URI
    655         Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
    656         Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
    657 
    658         assertDataRows(dataUri, values);
    659 
    660         // Complete but stale lookup URI
    661         lookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
    662         dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
    663         assertDataRows(dataUri, values);
    664 
    665         // Incomplete lookup URI (lookup key only, no contact ID)
    666         dataUri = Uri.withAppendedPath(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
    667                 lookupKey), Contacts.Data.CONTENT_DIRECTORY);
    668         assertDataRows(dataUri, values);
    669     }
    670 
    671     private void assertDataRows(Uri dataUri, ContentValues values) {
    672         Cursor cursor = mResolver.query(dataUri, new String[]{ Data.DATA1 }, null, null, Data._ID);
    673         assertEquals(3, cursor.getCount());
    674         cursor.moveToFirst();
    675         values.put(Data.DATA1, "John Doe");
    676         assertCursorValues(cursor, values);
    677 
    678         cursor.moveToNext();
    679         values.put(Data.DATA1, "555-GOOG-411");
    680         assertCursorValues(cursor, values);
    681 
    682         cursor.moveToNext();
    683         values.put(Data.DATA1, "google (at) android.com");
    684         assertCursorValues(cursor, values);
    685 
    686         cursor.close();
    687     }
    688 
    689     public void testContactEntitiesWithIdBasedUri() {
    690         ContentValues values = new ContentValues();
    691         Account account1 = new Account("act1", "actype1");
    692         Account account2 = new Account("act2", "actype2");
    693 
    694         long rawContactId1 = createRawContactWithName(account1);
    695         insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
    696         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90,
    697                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
    698 
    699         long rawContactId2 = createRawContact(account2);
    700         setAggregationException(
    701                 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
    702 
    703         long contactId = queryContactId(rawContactId1);
    704 
    705         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
    706         Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
    707 
    708         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
    709     }
    710 
    711     public void testContactEntitiesWithLookupUri() {
    712         ContentValues values = new ContentValues();
    713         Account account1 = new Account("act1", "actype1");
    714         Account account2 = new Account("act2", "actype2");
    715 
    716         long rawContactId1 = createRawContactWithName(account1);
    717         insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
    718         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90,
    719                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
    720 
    721         long rawContactId2 = createRawContact(account2);
    722         setAggregationException(
    723                 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
    724 
    725         long contactId = queryContactId(rawContactId1);
    726         String lookupKey = queryLookupKey(contactId);
    727 
    728         // First try with a matching contact ID
    729         Uri contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
    730         Uri entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
    731         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
    732 
    733         // Now try with a contact ID mismatch
    734         contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
    735         entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
    736         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
    737 
    738         // Now try without an ID altogether
    739         contactLookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
    740         entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
    741         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
    742     }
    743 
    744     private void assertEntityRows(Uri entityUri, long contactId, long rawContactId1,
    745             long rawContactId2) {
    746         ContentValues values = new ContentValues();
    747 
    748         Cursor cursor = mResolver.query(entityUri, null, null, null,
    749                 Contacts.Entity.RAW_CONTACT_ID + "," + Contacts.Entity.DATA_ID);
    750         assertEquals(3, cursor.getCount());
    751 
    752         // First row - name
    753         cursor.moveToFirst();
    754         values.put(Contacts.Entity.CONTACT_ID, contactId);
    755         values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1);
    756         values.put(Contacts.Entity.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
    757         values.put(Contacts.Entity.DATA1, "John Doe");
    758         values.put(Contacts.Entity.ACCOUNT_NAME, "act1");
    759         values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1");
    760         values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
    761         values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
    762         values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
    763         values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
    764         values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
    765         values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
    766         values.putNull(Contacts.Entity.PRESENCE);
    767         assertCursorValues(cursor, values);
    768 
    769         // Second row - IM
    770         cursor.moveToNext();
    771         values.put(Contacts.Entity.CONTACT_ID, contactId);
    772         values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1);
    773         values.put(Contacts.Entity.MIMETYPE, Im.CONTENT_ITEM_TYPE);
    774         values.put(Contacts.Entity.DATA1, "gtalk");
    775         values.put(Contacts.Entity.ACCOUNT_NAME, "act1");
    776         values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1");
    777         values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
    778         values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
    779         values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
    780         values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
    781         values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
    782         values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
    783         values.put(Contacts.Entity.PRESENCE, StatusUpdates.IDLE);
    784         assertCursorValues(cursor, values);
    785 
    786         // Third row - second raw contact, not data
    787         cursor.moveToNext();
    788         values.put(Contacts.Entity.CONTACT_ID, contactId);
    789         values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId2);
    790         values.putNull(Contacts.Entity.MIMETYPE);
    791         values.putNull(Contacts.Entity.DATA_ID);
    792         values.putNull(Contacts.Entity.DATA1);
    793         values.put(Contacts.Entity.ACCOUNT_NAME, "act2");
    794         values.put(Contacts.Entity.ACCOUNT_TYPE, "actype2");
    795         values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
    796         values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
    797         values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
    798         values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
    799         values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
    800         values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
    801         values.putNull(Contacts.Entity.PRESENCE);
    802         assertCursorValues(cursor, values);
    803 
    804         cursor.close();
    805     }
    806 
    807     public void testDataInsert() {
    808         long rawContactId = createRawContactWithName("John", "Doe");
    809 
    810         ContentValues values = new ContentValues();
    811         putDataValues(values, rawContactId);
    812         Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
    813         long dataId = ContentUris.parseId(dataUri);
    814 
    815         long contactId = queryContactId(rawContactId);
    816         values.put(RawContacts.CONTACT_ID, contactId);
    817         assertStoredValues(dataUri, values);
    818 
    819         assertSelection(Data.CONTENT_URI, values, Data._ID, dataId);
    820 
    821         // Access the same data through the directory under RawContacts
    822         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
    823         Uri rawContactDataUri =
    824                 Uri.withAppendedPath(rawContactUri, RawContacts.Data.CONTENT_DIRECTORY);
    825         assertSelection(rawContactDataUri, values, Data._ID, dataId);
    826 
    827         // Access the same data through the directory under Contacts
    828         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
    829         Uri contactDataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY);
    830         assertSelection(contactDataUri, values, Data._ID, dataId);
    831         assertNetworkNotified(true);
    832     }
    833 
    834     public void testRawContactDataQuery() {
    835         Account account1 = new Account("a", "b");
    836         Account account2 = new Account("c", "d");
    837         long rawContactId1 = createRawContact(account1);
    838         Uri dataUri1 = insertStructuredName(rawContactId1, "John", "Doe");
    839         long rawContactId2 = createRawContact(account2);
    840         Uri dataUri2 = insertStructuredName(rawContactId2, "Jane", "Doe");
    841 
    842         Uri uri1 = maybeAddAccountQueryParameters(dataUri1, account1);
    843         Uri uri2 = maybeAddAccountQueryParameters(dataUri2, account2);
    844         assertStoredValue(uri1, Data._ID, ContentUris.parseId(dataUri1)) ;
    845         assertStoredValue(uri2, Data._ID, ContentUris.parseId(dataUri2)) ;
    846     }
    847 
    848     public void testPhonesQuery() {
    849 
    850         ContentValues values = new ContentValues();
    851         values.put(RawContacts.CUSTOM_RINGTONE, "d");
    852         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
    853         values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
    854         values.put(RawContacts.TIMES_CONTACTED, 54321);
    855         values.put(RawContacts.STARRED, 1);
    856 
    857         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
    858         long rawContactId = ContentUris.parseId(rawContactUri);
    859 
    860         insertStructuredName(rawContactId, "Meghan", "Knox");
    861         Uri uri = insertPhoneNumber(rawContactId, "18004664411");
    862         long phoneId = ContentUris.parseId(uri);
    863 
    864 
    865         long contactId = queryContactId(rawContactId);
    866         values.clear();
    867         values.put(Data._ID, phoneId);
    868         values.put(Data.RAW_CONTACT_ID, rawContactId);
    869         values.put(RawContacts.CONTACT_ID, contactId);
    870         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    871         values.put(Phone.NUMBER, "18004664411");
    872         values.put(Phone.TYPE, Phone.TYPE_HOME);
    873         values.putNull(Phone.LABEL);
    874         values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
    875         values.put(Contacts.CUSTOM_RINGTONE, "d");
    876         values.put(Contacts.SEND_TO_VOICEMAIL, 1);
    877         values.put(Contacts.LAST_TIME_CONTACTED, 12345);
    878         values.put(Contacts.TIMES_CONTACTED, 54321);
    879         values.put(Contacts.STARRED, 1);
    880 
    881         assertStoredValues(ContentUris.withAppendedId(Phone.CONTENT_URI, phoneId), values);
    882         assertSelection(Phone.CONTENT_URI, values, Data._ID, phoneId);
    883     }
    884 
    885     public void testPhonesWithMergedContacts() {
    886         long rawContactId1 = createRawContact();
    887         insertPhoneNumber(rawContactId1, "123456789", true);
    888 
    889         long rawContactId2 = createRawContact();
    890         insertPhoneNumber(rawContactId2, "123456789", true);
    891 
    892         ContentValues values1 = new ContentValues();
    893         values1.put(Contacts.DISPLAY_NAME, "123456789");
    894         values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    895         values1.put(Phone.NUMBER, "123456789");
    896 
    897         // Two results should come, since they are separate entries anyway.
    898         assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1});
    899 
    900         // Even with remove_duplicate_entries flag, we should return two results here, because
    901         // they have different raw_contact_id-s.
    902         final Uri dedupeUri = Phone.CONTENT_URI.buildUpon()
    903                 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
    904                 .build();
    905         assertStoredValues(dedupeUri, new ContentValues[] {values1, values1});
    906 
    907         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
    908                 rawContactId1, rawContactId2);
    909 
    910         assertAggregated(rawContactId1, rawContactId2, "123456789");
    911 
    912         // Contact merge won't affect the default result of Phone Uri.
    913         assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1});
    914 
    915         // We should detect duplicates when requested.
    916         assertStoredValues(dedupeUri, values1);
    917     }
    918 
    919     public void testPhonesFilterQuery() {
    920         long rawContactId1 = createRawContactWithName("Hot", "Tamale", ACCOUNT_1);
    921         insertPhoneNumber(rawContactId1, "1-800-466-4411");
    922 
    923         long rawContactId2 = createRawContactWithName("Chilled", "Guacamole", ACCOUNT_2);
    924         insertPhoneNumber(rawContactId2, "1-800-466-5432");
    925         insertPhoneNumber(rawContactId2, "0 (at) example.com", false, Phone.TYPE_PAGER);
    926         insertPhoneNumber(rawContactId2, "1 (at) example.com", false, Phone.TYPE_PAGER);
    927 
    928         Uri filterUri1 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "tamale");
    929         ContentValues values = new ContentValues();
    930         values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
    931         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    932         values.put(Phone.NUMBER, "1-800-466-4411");
    933         values.put(Phone.TYPE, Phone.TYPE_HOME);
    934         values.putNull(Phone.LABEL);
    935         assertStoredValuesWithProjection(filterUri1, values);
    936 
    937         Uri filterUri2 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1-800-GOOG-411");
    938         assertStoredValues(filterUri2, values);
    939 
    940         Uri filterUri3 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "18004664");
    941         assertStoredValues(filterUri3, values);
    942 
    943         Uri filterUri4 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "encilada");
    944         assertEquals(0, getCount(filterUri4, null, null));
    945 
    946         Uri filterUri5 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "*");
    947         assertEquals(0, getCount(filterUri5, null, null));
    948 
    949         ContentValues values1 = new ContentValues();
    950         values1.put(Contacts.DISPLAY_NAME, "Chilled Guacamole");
    951         values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    952         values1.put(Phone.NUMBER, "1-800-466-5432");
    953         values1.put(Phone.TYPE, Phone.TYPE_HOME);
    954         values1.putNull(Phone.LABEL);
    955 
    956         ContentValues values2 = new ContentValues();
    957         values2.put(Contacts.DISPLAY_NAME, "Chilled Guacamole");
    958         values2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    959         values2.put(Phone.NUMBER, "0 (at) example.com");
    960         values2.put(Phone.TYPE, Phone.TYPE_PAGER);
    961         values2.putNull(Phone.LABEL);
    962 
    963         ContentValues values3 = new ContentValues();
    964         values3.put(Contacts.DISPLAY_NAME, "Chilled Guacamole");
    965         values3.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    966         values3.put(Phone.NUMBER, "1 (at) example.com");
    967         values3.put(Phone.TYPE, Phone.TYPE_PAGER);
    968         values3.putNull(Phone.LABEL);
    969 
    970         Uri filterUri6 = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "Chilled");
    971         assertStoredValues(filterUri6, new ContentValues[] {values1, values2, values3} );
    972     }
    973 
    974     public void testPhoneLookup() {
    975         ContentValues values = new ContentValues();
    976         values.put(RawContacts.CUSTOM_RINGTONE, "d");
    977         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
    978 
    979         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
    980         long rawContactId = ContentUris.parseId(rawContactUri);
    981 
    982         insertStructuredName(rawContactId, "Hot", "Tamale");
    983         insertPhoneNumber(rawContactId, "18004664411");
    984 
    985         Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
    986 
    987         values.clear();
    988         values.put(PhoneLookup._ID, queryContactId(rawContactId));
    989         values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale");
    990         values.put(PhoneLookup.NUMBER, "18004664411");
    991         values.put(PhoneLookup.TYPE, Phone.TYPE_HOME);
    992         values.putNull(PhoneLookup.LABEL);
    993         values.put(PhoneLookup.CUSTOM_RINGTONE, "d");
    994         values.put(PhoneLookup.SEND_TO_VOICEMAIL, 1);
    995         assertStoredValues(lookupUri1, values);
    996 
    997         // In the context that 8004664411 is a valid number, "4664411" as a
    998         // call id should  match to both "8004664411" and "+18004664411".
    999         Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411");
   1000         assertEquals(2, getCount(lookupUri2, null, null));
   1001     }
   1002 
   1003     public void testPhoneLookupUseCases() {
   1004         ContentValues values = new ContentValues();
   1005         Uri rawContactUri;
   1006         long rawContactId;
   1007         Uri lookupUri2;
   1008 
   1009         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1010         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1011 
   1012         // International format in contacts
   1013         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1014         rawContactId = ContentUris.parseId(rawContactUri);
   1015 
   1016         insertStructuredName(rawContactId, "Hot", "Tamale");
   1017         insertPhoneNumber(rawContactId, "+1-650-861-0000");
   1018 
   1019         values.clear();
   1020 
   1021         // match with international format
   1022         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0000");
   1023         assertEquals(1, getCount(lookupUri2, null, null));
   1024 
   1025         // match with national format
   1026         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0000");
   1027         assertEquals(1, getCount(lookupUri2, null, null));
   1028 
   1029         // National format in contacts
   1030         values.clear();
   1031         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1032         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1033         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1034         rawContactId = ContentUris.parseId(rawContactUri);
   1035 
   1036         insertStructuredName(rawContactId, "Hot1", "Tamale");
   1037         insertPhoneNumber(rawContactId, "650-861-0001");
   1038 
   1039         values.clear();
   1040 
   1041         // match with international format
   1042         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0001");
   1043         assertEquals(2, getCount(lookupUri2, null, null));
   1044 
   1045         // match with national format
   1046         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0001");
   1047         assertEquals(2, getCount(lookupUri2, null, null));
   1048 
   1049         // Local format in contacts
   1050         values.clear();
   1051         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1052         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1053         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1054         rawContactId = ContentUris.parseId(rawContactUri);
   1055 
   1056         insertStructuredName(rawContactId, "Hot2", "Tamale");
   1057         insertPhoneNumber(rawContactId, "861-0002");
   1058 
   1059         values.clear();
   1060 
   1061         // match with international format
   1062         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0002");
   1063         assertEquals(1, getCount(lookupUri2, null, null));
   1064 
   1065         // match with national format
   1066         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0002");
   1067         assertEquals(1, getCount(lookupUri2, null, null));
   1068     }
   1069 
   1070     public void testPhoneUpdate() {
   1071         ContentValues values = new ContentValues();
   1072         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1073         long rawContactId = ContentUris.parseId(rawContactUri);
   1074 
   1075         insertStructuredName(rawContactId, "Hot", "Tamale");
   1076         Uri phoneUri = insertPhoneNumber(rawContactId, "18004664411");
   1077 
   1078         Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
   1079         assertStoredValue(lookupUri1, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
   1080 
   1081         values.clear();
   1082         values.put(Phone.NUMBER, "18004664422");
   1083         mResolver.update(phoneUri, values, null, null);
   1084 
   1085         Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664422");
   1086         assertStoredValue(lookupUri2, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
   1087 
   1088         // Setting number to null will remove the phone lookup record
   1089         values.clear();
   1090         values.putNull(Phone.NUMBER);
   1091         mResolver.update(phoneUri, values, null, null);
   1092 
   1093         assertEquals(0, getCount(lookupUri2, null, null));
   1094 
   1095         // Let's restore that phone lookup record
   1096         values.clear();
   1097         values.put(Phone.NUMBER, "18004664422");
   1098         mResolver.update(phoneUri, values, null, null);
   1099         assertStoredValue(lookupUri2, PhoneLookup.DISPLAY_NAME, "Hot Tamale");
   1100         assertNetworkNotified(true);
   1101     }
   1102 
   1103     public void testEmailsQuery() {
   1104         ContentValues values = new ContentValues();
   1105         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1106         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1107         values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
   1108         values.put(RawContacts.TIMES_CONTACTED, 54321);
   1109         values.put(RawContacts.STARRED, 1);
   1110 
   1111         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1112         final long rawContactId = ContentUris.parseId(rawContactUri);
   1113 
   1114         insertStructuredName(rawContactId, "Meghan", "Knox");
   1115         final Uri emailUri = insertEmail(rawContactId, "meghan (at) acme.com");
   1116         final long emailId = ContentUris.parseId(emailUri);
   1117 
   1118         final long contactId = queryContactId(rawContactId);
   1119         values.clear();
   1120         values.put(Data._ID, emailId);
   1121         values.put(Data.RAW_CONTACT_ID, rawContactId);
   1122         values.put(RawContacts.CONTACT_ID, contactId);
   1123         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   1124         values.put(Email.DATA, "meghan (at) acme.com");
   1125         values.put(Email.TYPE, Email.TYPE_HOME);
   1126         values.putNull(Email.LABEL);
   1127         values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
   1128         values.put(Contacts.CUSTOM_RINGTONE, "d");
   1129         values.put(Contacts.SEND_TO_VOICEMAIL, 1);
   1130         values.put(Contacts.LAST_TIME_CONTACTED, 12345);
   1131         values.put(Contacts.TIMES_CONTACTED, 54321);
   1132         values.put(Contacts.STARRED, 1);
   1133 
   1134         assertStoredValues(Email.CONTENT_URI, values);
   1135         assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values);
   1136         assertSelection(Email.CONTENT_URI, values, Data._ID, emailId);
   1137 
   1138         // Check if the provider detects duplicated email addresses.
   1139         final Uri emailUri2 = insertEmail(rawContactId, "meghan (at) acme.com");
   1140         final long emailId2 = ContentUris.parseId(emailUri2);
   1141         final ContentValues values2 = new ContentValues(values);
   1142         values2.put(Data._ID, emailId2);
   1143 
   1144         final Uri dedupeUri = Email.CONTENT_URI.buildUpon()
   1145                 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
   1146                 .build();
   1147 
   1148         // URI with ID should return a correct result.
   1149         assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values);
   1150         assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId), values);
   1151         assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId2), values2);
   1152         assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId2), values2);
   1153 
   1154         assertStoredValues(Email.CONTENT_URI, new ContentValues[] {values, values2});
   1155 
   1156         // If requested to remove duplicates, the query should return just one result,
   1157         // whose _ID won't be deterministic.
   1158         values.remove(Data._ID);
   1159         assertStoredValues(dedupeUri, values);
   1160     }
   1161 
   1162     public void testEmailsLookupQuery() {
   1163         long rawContactId = createRawContactWithName("Hot", "Tamale");
   1164         insertEmail(rawContactId, "tamale (at) acme.com");
   1165 
   1166         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "tamale (at) acme.com");
   1167         ContentValues values = new ContentValues();
   1168         values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   1169         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   1170         values.put(Email.DATA, "tamale (at) acme.com");
   1171         values.put(Email.TYPE, Email.TYPE_HOME);
   1172         values.putNull(Email.LABEL);
   1173         assertStoredValues(filterUri1, values);
   1174 
   1175         Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "Ta<TaMale (at) acme.com>");
   1176         assertStoredValues(filterUri2, values);
   1177 
   1178         Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "encilada (at) acme.com");
   1179         assertEquals(0, getCount(filterUri3, null, null));
   1180     }
   1181 
   1182     public void testEmailsFilterQuery() {
   1183         long rawContactId1 = createRawContactWithName("Hot", "Tamale", ACCOUNT_1);
   1184         insertEmail(rawContactId1, "tamale (at) acme.com");
   1185         insertEmail(rawContactId1, "tamale (at) acme.com");
   1186 
   1187         long rawContactId2 = createRawContactWithName("Hot", "Tamale", ACCOUNT_2);
   1188         insertEmail(rawContactId2, "tamale (at) acme.com");
   1189 
   1190         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tam");
   1191         ContentValues values = new ContentValues();
   1192         values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   1193         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   1194         values.put(Email.DATA, "tamale (at) acme.com");
   1195         values.put(Email.TYPE, Email.TYPE_HOME);
   1196         values.putNull(Email.LABEL);
   1197         assertStoredValuesWithProjection(filterUri1, values);
   1198 
   1199         Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot");
   1200         assertStoredValuesWithProjection(filterUri2, values);
   1201 
   1202         Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot tamale");
   1203         assertStoredValuesWithProjection(filterUri3, values);
   1204 
   1205         Uri filterUri4 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tamale@acme");
   1206         assertStoredValuesWithProjection(filterUri4, values);
   1207 
   1208         Uri filterUri5 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "encilada");
   1209         assertEquals(0, getCount(filterUri5, null, null));
   1210     }
   1211 
   1212     /**
   1213      * Tests if ContactsProvider2 returns addresses according to registration order.
   1214      */
   1215     public void testEmailFilterDefaultSortOrder() {
   1216         long rawContactId1 = createRawContact();
   1217         insertEmail(rawContactId1, "address1 (at) email.com");
   1218         insertEmail(rawContactId1, "address2 (at) email.com");
   1219         insertEmail(rawContactId1, "address3 (at) email.com");
   1220         ContentValues v1 = new ContentValues();
   1221         v1.put(Email.ADDRESS, "address1 (at) email.com");
   1222         ContentValues v2 = new ContentValues();
   1223         v2.put(Email.ADDRESS, "address2 (at) email.com");
   1224         ContentValues v3 = new ContentValues();
   1225         v3.put(Email.ADDRESS, "address3 (at) email.com");
   1226 
   1227         Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
   1228         assertStoredValuesOrderly(filterUri, new ContentValues[] { v1, v2, v3 });
   1229     }
   1230 
   1231     /**
   1232      * Tests if ContactsProvider2 returns primary addresses before the other addresses.
   1233      */
   1234     public void testEmailFilterPrimaryAddress() {
   1235         long rawContactId1 = createRawContact();
   1236         insertEmail(rawContactId1, "address1 (at) email.com");
   1237         insertEmail(rawContactId1, "address2 (at) email.com", true);
   1238         ContentValues v1 = new ContentValues();
   1239         v1.put(Email.ADDRESS, "address1 (at) email.com");
   1240         ContentValues v2 = new ContentValues();
   1241         v2.put(Email.ADDRESS, "address2 (at) email.com");
   1242 
   1243         Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
   1244         assertStoredValuesOrderly(filterUri, new ContentValues[] { v2, v1 });
   1245     }
   1246 
   1247     /**
   1248      * Tests if ContactsProvider2 has email address associated with a primary account before the
   1249      * other address.
   1250      */
   1251     public void testEmailFilterPrimaryAccount() {
   1252         long rawContactId1 = createRawContact(ACCOUNT_1);
   1253         insertEmail(rawContactId1, "account1 (at) email.com");
   1254         long rawContactId2 = createRawContact(ACCOUNT_2);
   1255         insertEmail(rawContactId2, "account2 (at) email.com");
   1256         ContentValues v1 = new ContentValues();
   1257         v1.put(Email.ADDRESS, "account1 (at) email.com");
   1258         ContentValues v2 = new ContentValues();
   1259         v2.put(Email.ADDRESS, "account2 (at) email.com");
   1260 
   1261         Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
   1262                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_1.name)
   1263                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, ACCOUNT_1.type)
   1264                 .build();
   1265         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2 });
   1266 
   1267         Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
   1268                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_2.name)
   1269                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, ACCOUNT_2.type)
   1270                 .build();
   1271         assertStoredValuesOrderly(filterUri2, new ContentValues[] { v2, v1 });
   1272 
   1273         // Just with PRIMARY_ACCOUNT_NAME
   1274         Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
   1275                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_1.name)
   1276                 .build();
   1277         assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2 });
   1278 
   1279         Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
   1280                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, ACCOUNT_2.name)
   1281                 .build();
   1282         assertStoredValuesOrderly(filterUri4, new ContentValues[] { v2, v1 });
   1283     }
   1284 
   1285     /** Tests {@link DataUsageFeedback} correctly promotes a data row instead of a raw contact. */
   1286     public void testEmailFilterSortOrderWithFeedback() {
   1287         long rawContactId1 = createRawContact();
   1288         String address1 = "address1 (at) email.com";
   1289         insertEmail(rawContactId1, address1);
   1290         long rawContactId2 = createRawContact();
   1291         String address2 = "address2 (at) email.com";
   1292         insertEmail(rawContactId2, address2);
   1293         String address3 = "address3 (at) email.com";
   1294         ContentUris.parseId(insertEmail(rawContactId2, address3));
   1295 
   1296         ContentValues v1 = new ContentValues();
   1297         v1.put(Email.ADDRESS, "address1 (at) email.com");
   1298         ContentValues v2 = new ContentValues();
   1299         v2.put(Email.ADDRESS, "address2 (at) email.com");
   1300         ContentValues v3 = new ContentValues();
   1301         v3.put(Email.ADDRESS, "address3 (at) email.com");
   1302 
   1303         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
   1304         Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
   1305                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
   1306                         DataUsageFeedback.USAGE_TYPE_CALL)
   1307                 .build();
   1308         Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
   1309                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
   1310                         DataUsageFeedback.USAGE_TYPE_LONG_TEXT)
   1311                 .build();
   1312         Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
   1313                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
   1314                         DataUsageFeedback.USAGE_TYPE_SHORT_TEXT)
   1315                 .build();
   1316         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2, v3 });
   1317         assertStoredValuesOrderly(filterUri2, new ContentValues[] { v1, v2, v3 });
   1318         assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2, v3 });
   1319         assertStoredValuesOrderly(filterUri4, new ContentValues[] { v1, v2, v3 });
   1320 
   1321         sendFeedback(address3, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, v3);
   1322 
   1323         // account3 (at) email.com should be the first. account2 (at) email.com should also be promoted as
   1324         // it has same contact id.
   1325         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v3, v1, v2 });
   1326         assertStoredValuesOrderly(filterUri3, new ContentValues[] { v3, v1, v2 });
   1327     }
   1328 
   1329     /**
   1330      * Tests {@link DataUsageFeedback} correctly bucketize contacts using each
   1331      * {@link DataUsageStatColumns#LAST_TIME_USED}
   1332      */
   1333     public void testEmailFilterSortOrderWithOldHistory() {
   1334         long rawContactId1 = createRawContact();
   1335         long dataId1 = ContentUris.parseId(insertEmail(rawContactId1, "address1 (at) email.com"));
   1336         long dataId2 = ContentUris.parseId(insertEmail(rawContactId1, "address2 (at) email.com"));
   1337         long dataId3 = ContentUris.parseId(insertEmail(rawContactId1, "address3 (at) email.com"));
   1338         long dataId4 = ContentUris.parseId(insertEmail(rawContactId1, "address4 (at) email.com"));
   1339 
   1340         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
   1341 
   1342         ContentValues v1 = new ContentValues();
   1343         v1.put(Email.ADDRESS, "address1 (at) email.com");
   1344         ContentValues v2 = new ContentValues();
   1345         v2.put(Email.ADDRESS, "address2 (at) email.com");
   1346         ContentValues v3 = new ContentValues();
   1347         v3.put(Email.ADDRESS, "address3 (at) email.com");
   1348         ContentValues v4 = new ContentValues();
   1349         v4.put(Email.ADDRESS, "address4 (at) email.com");
   1350 
   1351         final ContactsProvider2 provider = (ContactsProvider2) getProvider();
   1352 
   1353         long nowInMillis = System.currentTimeMillis();
   1354         long yesterdayInMillis = (nowInMillis - 24 * 60 * 60 * 1000);
   1355         long sevenDaysAgoInMillis = (nowInMillis - 7 * 24 * 60 * 60 * 1000);
   1356         long oneYearAgoInMillis = (nowInMillis - 365L * 24 * 60 * 60 * 1000);
   1357 
   1358         // address4 is contacted just once yesterday.
   1359         provider.updateDataUsageStat(Arrays.asList(dataId4),
   1360                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, yesterdayInMillis);
   1361 
   1362         // address3 is contacted twice 1 week ago.
   1363         provider.updateDataUsageStat(Arrays.asList(dataId3),
   1364                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis);
   1365         provider.updateDataUsageStat(Arrays.asList(dataId3),
   1366                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis);
   1367 
   1368         // address2 is contacted three times 1 year ago.
   1369         provider.updateDataUsageStat(Arrays.asList(dataId2),
   1370                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
   1371         provider.updateDataUsageStat(Arrays.asList(dataId2),
   1372                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
   1373         provider.updateDataUsageStat(Arrays.asList(dataId2),
   1374                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
   1375 
   1376         // auto-complete should prefer recently contacted methods
   1377         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v4, v3, v2, v1 });
   1378 
   1379         // Pretend address2 is contacted right now
   1380         provider.updateDataUsageStat(Arrays.asList(dataId2),
   1381                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis);
   1382 
   1383         // Now address2 is the most recently used address
   1384         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v4, v3, v1 });
   1385 
   1386         // Pretend address1 is contacted right now
   1387         provider.updateDataUsageStat(Arrays.asList(dataId1),
   1388                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis);
   1389 
   1390         // address2 is preferred to address1 as address2 is used 4 times in total
   1391         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v1, v4, v3 });
   1392     }
   1393 
   1394     public void testPostalsQuery() {
   1395         long rawContactId = createRawContactWithName("Alice", "Nextore");
   1396         Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");
   1397         final long dataId = ContentUris.parseId(dataUri);
   1398 
   1399         final long contactId = queryContactId(rawContactId);
   1400         ContentValues values = new ContentValues();
   1401         values.put(Data._ID, dataId);
   1402         values.put(Data.RAW_CONTACT_ID, rawContactId);
   1403         values.put(RawContacts.CONTACT_ID, contactId);
   1404         values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
   1405         values.put(StructuredPostal.FORMATTED_ADDRESS, "1600 Amphiteatre Ave, Mountain View");
   1406         values.put(Contacts.DISPLAY_NAME, "Alice Nextore");
   1407 
   1408         assertStoredValues(StructuredPostal.CONTENT_URI, values);
   1409         assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId),
   1410                 values);
   1411         assertSelection(StructuredPostal.CONTENT_URI, values, Data._ID, dataId);
   1412 
   1413         // Check if the provider detects duplicated addresses.
   1414         Uri dataUri2 = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");
   1415         final long dataId2 = ContentUris.parseId(dataUri2);
   1416         final ContentValues values2 = new ContentValues(values);
   1417         values2.put(Data._ID, dataId2);
   1418 
   1419         final Uri dedupeUri = StructuredPostal.CONTENT_URI.buildUpon()
   1420                 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
   1421                 .build();
   1422 
   1423         // URI with ID should return a correct result.
   1424         assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId),
   1425                 values);
   1426         assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId), values);
   1427         assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId2),
   1428                 values2);
   1429         assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId2), values2);
   1430 
   1431         assertStoredValues(StructuredPostal.CONTENT_URI, new ContentValues[] {values, values2});
   1432 
   1433         // If requested to remove duplicates, the query should return just one result,
   1434         // whose _ID won't be deterministic.
   1435         values.remove(Data._ID);
   1436         assertStoredValues(dedupeUri, values);
   1437     }
   1438 
   1439     public void testQueryContactData() {
   1440         ContentValues values = new ContentValues();
   1441         long contactId = createContact(values, "John", "Doe",
   1442                 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   1443                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
   1444         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   1445 
   1446         assertStoredValues(contactUri, values);
   1447         assertSelection(Contacts.CONTENT_URI, values, Contacts._ID, contactId);
   1448     }
   1449 
   1450     public void testQueryContactWithStatusUpdate() {
   1451         ContentValues values = new ContentValues();
   1452         long contactId = createContact(values, "John", "Doe",
   1453                 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   1454                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   1455         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
   1456         values.put(Contacts.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
   1457         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   1458         assertStoredValuesWithProjection(contactUri, values);
   1459         assertSelectionWithProjection(Contacts.CONTENT_URI, values, Contacts._ID, contactId);
   1460     }
   1461 
   1462     public void testQueryContactFilterByName() {
   1463         ContentValues values = new ContentValues();
   1464         long rawContactId = createRawContact(values, "18004664411",
   1465                 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   1466                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
   1467                 StatusUpdates.CAPABILITY_HAS_VOICE);
   1468 
   1469         ContentValues nameValues = new ContentValues();
   1470         nameValues.put(StructuredName.GIVEN_NAME, "Stu");
   1471         nameValues.put(StructuredName.FAMILY_NAME, "Goulash");
   1472         nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "goo");
   1473         nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "LASH");
   1474         Uri nameUri = insertStructuredName(rawContactId, nameValues);
   1475 
   1476         long contactId = queryContactId(rawContactId);
   1477         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
   1478 
   1479         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goulash");
   1480         assertStoredValuesWithProjection(filterUri1, values);
   1481 
   1482         assertContactFilter(contactId, "goolash");
   1483         assertContactFilter(contactId, "lash");
   1484 
   1485         assertContactFilterNoResult("goolish");
   1486 
   1487         // Phonetic name with given/family reversed should not match
   1488         assertContactFilterNoResult("lashgoo");
   1489 
   1490         nameValues.clear();
   1491         nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "ga");
   1492         nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "losh");
   1493 
   1494         mResolver.update(nameUri, nameValues, null, null);
   1495 
   1496         assertContactFilter(contactId, "galosh");
   1497 
   1498         assertContactFilterNoResult("goolish");
   1499     }
   1500 
   1501     public void testQueryContactFilterByEmailAddress() {
   1502         ContentValues values = new ContentValues();
   1503         long rawContactId = createRawContact(values, "18004664411",
   1504                 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   1505                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
   1506                 StatusUpdates.CAPABILITY_HAS_VOICE);
   1507 
   1508         insertStructuredName(rawContactId, "James", "Bond");
   1509 
   1510         long contactId = queryContactId(rawContactId);
   1511         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
   1512 
   1513         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goog411 (at) acme.com");
   1514         assertStoredValuesWithProjection(filterUri1, values);
   1515 
   1516         assertContactFilter(contactId, "goog");
   1517         assertContactFilter(contactId, "goog411");
   1518         assertContactFilter(contactId, "goog411@");
   1519         assertContactFilter(contactId, "goog411@acme");
   1520         assertContactFilter(contactId, "goog411 (at) acme.com");
   1521 
   1522         assertContactFilterNoResult("goog411 (at) acme.combo");
   1523         assertContactFilterNoResult("goog411 (at) le.com");
   1524         assertContactFilterNoResult("goolish");
   1525     }
   1526 
   1527     public void testQueryContactFilterByPhoneNumber() {
   1528         ContentValues values = new ContentValues();
   1529         long rawContactId = createRawContact(values, "18004664411",
   1530                 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   1531                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
   1532                 StatusUpdates.CAPABILITY_HAS_VOICE);
   1533 
   1534         insertStructuredName(rawContactId, "James", "Bond");
   1535 
   1536         long contactId = queryContactId(rawContactId);
   1537         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
   1538 
   1539         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "18004664411");
   1540         assertStoredValuesWithProjection(filterUri1, values);
   1541 
   1542         assertContactFilter(contactId, "18004664411");
   1543         assertContactFilter(contactId, "1800466");
   1544         assertContactFilter(contactId, "+18004664411");
   1545         assertContactFilter(contactId, "8004664411");
   1546 
   1547         assertContactFilterNoResult("78004664411");
   1548         assertContactFilterNoResult("18004664412");
   1549         assertContactFilterNoResult("8884664411");
   1550     }
   1551 
   1552     /**
   1553      * Checks ContactsProvider2 works well with strequent Uris. The provider should return starred
   1554      * contacts and frequently used contacts.
   1555      */
   1556     public void testQueryContactStrequent() {
   1557         ContentValues values1 = new ContentValues();
   1558         final String email1 = "a (at) acme.com";
   1559         final int timesContacted1 = 0;
   1560         createContact(values1, "Noah", "Tever", "18004664411",
   1561                 email1, StatusUpdates.OFFLINE, timesContacted1, 0, 0,
   1562                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
   1563         final String phoneNumber2 = "18004664412";
   1564         ContentValues values2 = new ContentValues();
   1565         createContact(values2, "Sam", "Times", phoneNumber2,
   1566                 "b (at) acme.com", StatusUpdates.INVISIBLE, 3, 0, 0,
   1567                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   1568         ContentValues values3 = new ContentValues();
   1569         final String phoneNumber3 = "18004664413";
   1570         final int timesContacted3 = 5;
   1571         createContact(values3, "Lotta", "Calling", phoneNumber3,
   1572                 "c (at) acme.com", StatusUpdates.AWAY, timesContacted3, 0, 0,
   1573                 StatusUpdates.CAPABILITY_HAS_VIDEO);
   1574         ContentValues values4 = new ContentValues();
   1575         final long rawContactId4 = createRawContact(values4, "Fay", "Veritt", null,
   1576                 "d (at) acme.com", StatusUpdates.AVAILABLE, 0, 1, 0,
   1577                 StatusUpdates.CAPABILITY_HAS_VIDEO | StatusUpdates.CAPABILITY_HAS_VOICE);
   1578 
   1579         // Starred contacts should be returned. TIMES_CONTACTED should be ignored and only data
   1580         // usage feedback should be used for "frequently contacted" listing.
   1581         assertStoredValues(Contacts.CONTENT_STREQUENT_URI, values4);
   1582 
   1583         // Send feedback for the 3rd phone number, pretending we called that person via phone.
   1584         sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
   1585 
   1586         // After the feedback, 3rd contact should be shown after starred one.
   1587         assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI,
   1588                 new ContentValues[] { values4, values3 });
   1589 
   1590         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
   1591         // Twice.
   1592         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
   1593 
   1594         // After the feedback, 1st and 3rd contacts should be shown after starred one.
   1595         assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI,
   1596                 new ContentValues[] { values4, values1, values3 });
   1597 
   1598         // With phone-only parameter, 1st and 4th contacts shouldn't be returned because:
   1599         // 1st: feedbacks are only about email, not about phone call.
   1600         // 4th: it has no phone number though starred.
   1601         Uri phoneOnlyStrequentUri = Contacts.CONTENT_STREQUENT_URI.buildUpon()
   1602                 .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true")
   1603                 .build();
   1604         assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { values3 });
   1605 
   1606         // Now the 4th contact has a phone number.
   1607         insertPhoneNumber(rawContactId4, "18004664414");
   1608 
   1609         // Phone only strequent should return 4th contact.
   1610         assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { values4, values3 });
   1611 
   1612         // Send feedback for the 2rd phone number, pretending we send the person a SMS message.
   1613         sendFeedback(phoneNumber2, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1);
   1614 
   1615         // SMS feedback shouldn't affect phone-only results.
   1616         assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { values4, values3 });
   1617 
   1618         Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_STREQUENT_FILTER_URI, "fay");
   1619         assertStoredValues(filterUri, values4);
   1620     }
   1621 
   1622     /**
   1623      * Checks ContactsProvider2 works well with frequent Uri. The provider should return frequently
   1624      * contacted person ordered by number of times contacted.
   1625      */
   1626     public void testQueryContactFrequent() {
   1627         ContentValues values1 = new ContentValues();
   1628         final String email1 = "a (at) acme.com";
   1629         createContact(values1, "Noah", "Tever", "18004664411",
   1630                 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0);
   1631         ContentValues values2 = new ContentValues();
   1632         final String email2 = "b (at) acme.com";
   1633         createContact(values2, "Sam", "Times", "18004664412",
   1634                 email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0);
   1635         ContentValues values3 = new ContentValues();
   1636         final String phoneNumber3 = "18004664413";
   1637         final long contactId3 = createContact(values3, "Lotta", "Calling", phoneNumber3,
   1638                 "c (at) acme.com", StatusUpdates.AWAY, 0, 1, 0, 0);
   1639         ContentValues values4 = new ContentValues();
   1640         createContact(values4, "Fay", "Veritt", "18004664414",
   1641                 "d (at) acme.com", StatusUpdates.AVAILABLE, 0, 1, 0, 0);
   1642 
   1643         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
   1644 
   1645         assertStoredValues(Contacts.CONTENT_FREQUENT_URI, values1);
   1646 
   1647         // Pretend email was sent to the address twice.
   1648         sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2);
   1649         sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2);
   1650 
   1651         assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values2, values1});
   1652 
   1653         // Three times
   1654         sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
   1655         sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
   1656         sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
   1657 
   1658         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
   1659                 new ContentValues[] {values3, values2, values1});
   1660 
   1661         // Test it works with selection/selectionArgs
   1662         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
   1663                 Contacts.STARRED + "=?", new String[] {"0"},
   1664                 new ContentValues[] {values2, values1});
   1665         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
   1666                 Contacts.STARRED + "=?", new String[] {"1"},
   1667                 new ContentValues[] {values3});
   1668 
   1669         values3.put(Contacts.STARRED, 0);
   1670         assertEquals(1,
   1671                 mResolver.update(Uri.withAppendedPath(Contacts.CONTENT_URI,
   1672                         String.valueOf(contactId3)),
   1673                 values3, null, null));
   1674         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
   1675                 Contacts.STARRED + "=?", new String[] {"0"},
   1676                 new ContentValues[] {values3, values2, values1});
   1677         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
   1678                 Contacts.STARRED + "=?", new String[] {"1"},
   1679                 new ContentValues[] {});
   1680     }
   1681 
   1682     public void testQueryContactGroup() {
   1683         long groupId = createGroup(null, "testGroup", "Test Group");
   1684 
   1685         ContentValues values1 = new ContentValues();
   1686         createContact(values1, "Best", "West", "18004664411",
   1687                 "west (at) acme.com", StatusUpdates.OFFLINE, 0, 0, groupId,
   1688                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   1689 
   1690         ContentValues values2 = new ContentValues();
   1691         createContact(values2, "Rest", "East", "18004664422",
   1692                 "east (at) acme.com", StatusUpdates.AVAILABLE, 0, 0, 0,
   1693                 StatusUpdates.CAPABILITY_HAS_VOICE);
   1694 
   1695         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
   1696         Cursor c = mResolver.query(filterUri1, null, null, null, Contacts._ID);
   1697         assertEquals(1, c.getCount());
   1698         c.moveToFirst();
   1699         assertCursorValues(c, values1);
   1700         c.close();
   1701 
   1702         Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
   1703         c = mResolver.query(filterUri2, null, Contacts.DISPLAY_NAME + "=?",
   1704                 new String[] { "Best West" }, Contacts._ID);
   1705         assertEquals(1, c.getCount());
   1706         c.close();
   1707 
   1708         Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Next Group");
   1709         c = mResolver.query(filterUri3, null, null, null, Contacts._ID);
   1710         assertEquals(0, c.getCount());
   1711         c.close();
   1712     }
   1713 
   1714     private void expectSecurityException(String failureMessage, Uri uri, String[] projection,
   1715             String selection, String[] selectionArgs, String sortOrder) {
   1716         Cursor c = null;
   1717         try {
   1718             c = mResolver.query(uri, projection, selection, selectionArgs, sortOrder);
   1719             fail(failureMessage);
   1720         } catch (SecurityException expected) {
   1721             // The security exception is expected to occur because we're missing a permission.
   1722         } finally {
   1723             if (c != null) {
   1724                 c.close();
   1725             }
   1726         }
   1727     }
   1728 
   1729     public void testQueryProfileRequiresReadPermission() {
   1730         mActor.removePermissions("android.permission.READ_PROFILE");
   1731 
   1732         createBasicProfileContact(new ContentValues());
   1733 
   1734         // Case 1: Retrieving profile contact.
   1735         expectSecurityException(
   1736                 "Querying for the profile without READ_PROFILE access should fail.",
   1737                 Profile.CONTENT_URI, null, null, null, Contacts._ID);
   1738 
   1739         // Case 2: Retrieving profile data.
   1740         expectSecurityException(
   1741                 "Querying for the profile data without READ_PROFILE access should fail.",
   1742                 Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
   1743                 null, null, null, Contacts._ID);
   1744 
   1745         // Case 3: Retrieving profile entities.
   1746         expectSecurityException(
   1747                 "Querying for the profile entities without READ_PROFILE access should fail.",
   1748                 Profile.CONTENT_URI.buildUpon()
   1749                         .appendPath("entities").build(), null, null, null, Contacts._ID);
   1750     }
   1751 
   1752     public void testQueryProfileByContactIdRequiresReadPermission() {
   1753         long profileRawContactId = createBasicProfileContact(new ContentValues());
   1754         long profileContactId = queryContactId(profileRawContactId);
   1755 
   1756         mActor.removePermissions("android.permission.READ_PROFILE");
   1757 
   1758         // A query for the profile contact by ID should fail.
   1759         expectSecurityException(
   1760                 "Querying for the profile by contact ID without READ_PROFILE access should fail.",
   1761                 ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId),
   1762                 null, null, null, Contacts._ID);
   1763     }
   1764 
   1765     public void testQueryProfileByRawContactIdRequiresReadPermission() {
   1766         long profileRawContactId = createBasicProfileContact(new ContentValues());
   1767 
   1768         // Remove profile read permission and attempt to retrieve the raw contact.
   1769         mActor.removePermissions("android.permission.READ_PROFILE");
   1770         expectSecurityException(
   1771                 "Querying for the raw contact profile without READ_PROFILE access should fail.",
   1772                 ContentUris.withAppendedId(RawContacts.CONTENT_URI,
   1773                         profileRawContactId), null, null, null, RawContacts._ID);
   1774     }
   1775 
   1776     public void testQueryProfileRawContactRequiresReadPermission() {
   1777         long profileRawContactId = createBasicProfileContact(new ContentValues());
   1778 
   1779         // Remove profile read permission and attempt to retrieve the profile's raw contact data.
   1780         mActor.removePermissions("android.permission.READ_PROFILE");
   1781 
   1782         // Case 1: Retrieve the overall raw contact set for the profile.
   1783         expectSecurityException(
   1784                 "Querying for the raw contact profile without READ_PROFILE access should fail.",
   1785                 Profile.CONTENT_RAW_CONTACTS_URI, null, null, null, null);
   1786 
   1787         // Case 2: Retrieve the raw contact profile data for the inserted raw contact ID.
   1788         expectSecurityException(
   1789                 "Querying for the raw profile data without READ_PROFILE access should fail.",
   1790                 ContentUris.withAppendedId(
   1791                         Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
   1792                         .appendPath("data").build(), null, null, null, null);
   1793 
   1794         // Case 3: Retrieve the raw contact profile entity for the inserted raw contact ID.
   1795         expectSecurityException(
   1796                 "Querying for the raw profile entities without READ_PROFILE access should fail.",
   1797                 ContentUris.withAppendedId(
   1798                         Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
   1799                         .appendPath("entity").build(), null, null, null, null);
   1800     }
   1801 
   1802     public void testQueryProfileDataByDataIdRequiresReadPermission() {
   1803         createBasicProfileContact(new ContentValues());
   1804         Cursor c = mResolver.query(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
   1805                 new String[]{Data._ID, Data.MIMETYPE}, null, null, null);
   1806         assertEquals(4, c.getCount());  // Photo, phone, email, name.
   1807         c.moveToFirst();
   1808         long profileDataId = c.getLong(0);
   1809         c.close();
   1810 
   1811         // Remove profile read permission and attempt to retrieve the data
   1812         mActor.removePermissions("android.permission.READ_PROFILE");
   1813         expectSecurityException(
   1814                 "Querying for the data in the profile without READ_PROFILE access should fail.",
   1815                 ContentUris.withAppendedId(Data.CONTENT_URI, profileDataId),
   1816                 null, null, null, null);
   1817     }
   1818 
   1819     public void testQueryProfileDataRequiresReadPermission() {
   1820         createBasicProfileContact(new ContentValues());
   1821 
   1822         // Remove profile read permission and attempt to retrieve all profile data.
   1823         mActor.removePermissions("android.permission.READ_PROFILE");
   1824         expectSecurityException(
   1825                 "Querying for the data in the profile without READ_PROFILE access should fail.",
   1826                 Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
   1827                 null, null, null, null);
   1828     }
   1829 
   1830     public void testInsertProfileRequiresWritePermission() {
   1831         mActor.removePermissions("android.permission.WRITE_PROFILE");
   1832 
   1833         // Creating a non-profile contact should be fine.
   1834         createBasicNonProfileContact(new ContentValues());
   1835 
   1836         // Creating a profile contact should throw an exception.
   1837         try {
   1838             createBasicProfileContact(new ContentValues());
   1839             fail("Creating a profile contact should fail without WRITE_PROFILE access.");
   1840         } catch (SecurityException expected) {
   1841         }
   1842     }
   1843 
   1844     public void testInsertProfileDataRequiresWritePermission() {
   1845         long profileRawContactId = createBasicProfileContact(new ContentValues());
   1846 
   1847         mActor.removePermissions("android.permission.WRITE_PROFILE");
   1848         try {
   1849             insertEmail(profileRawContactId, "foo (at) bar.net", false);
   1850             fail("Inserting data into a profile contact should fail without WRITE_PROFILE access.");
   1851         } catch (SecurityException expected) {
   1852         }
   1853     }
   1854 
   1855     public void testUpdateDataDoesNotRequireProfilePermission() {
   1856         mActor.removePermissions("android.permission.READ_PROFILE");
   1857         mActor.removePermissions("android.permission.WRITE_PROFILE");
   1858 
   1859         // Create a non-profile contact.
   1860         long rawContactId = createRawContactWithName("Domo", "Arigato");
   1861         long dataId = getStoredLongValue(Data.CONTENT_URI,
   1862                 Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
   1863                 new String[]{String.valueOf(rawContactId), StructuredName.CONTENT_ITEM_TYPE},
   1864                 Data._ID);
   1865 
   1866         // Updates its name using a selection.
   1867         ContentValues values = new ContentValues();
   1868         values.put(StructuredName.GIVEN_NAME, "Bob");
   1869         values.put(StructuredName.FAMILY_NAME, "Blob");
   1870         mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?",
   1871                 new String[]{String.valueOf(dataId)});
   1872 
   1873         // Check that the update went through.
   1874         assertStoredValues(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), values);
   1875     }
   1876 
   1877     public void testQueryContactThenProfile() {
   1878         ContentValues profileValues = new ContentValues();
   1879         long profileRawContactId = createBasicProfileContact(profileValues);
   1880         long profileContactId = queryContactId(profileRawContactId);
   1881 
   1882         ContentValues nonProfileValues = new ContentValues();
   1883         long nonProfileRawContactId = createBasicNonProfileContact(nonProfileValues);
   1884         long nonProfileContactId = queryContactId(nonProfileRawContactId);
   1885 
   1886         assertStoredValues(Contacts.CONTENT_URI, nonProfileValues);
   1887         assertSelection(Contacts.CONTENT_URI, nonProfileValues, Contacts._ID, nonProfileContactId);
   1888 
   1889         assertStoredValues(Profile.CONTENT_URI, profileValues);
   1890     }
   1891 
   1892     public void testQueryContactExcludeProfile() {
   1893         // Create a profile contact (it should not be returned by the general contact URI).
   1894         createBasicProfileContact(new ContentValues());
   1895 
   1896         // Create a non-profile contact - this should be returned.
   1897         ContentValues nonProfileValues = new ContentValues();
   1898         createBasicNonProfileContact(nonProfileValues);
   1899 
   1900         assertStoredValues(Contacts.CONTENT_URI, new ContentValues[] {nonProfileValues});
   1901     }
   1902 
   1903     public void testQueryProfile() {
   1904         ContentValues profileValues = new ContentValues();
   1905         createBasicProfileContact(profileValues);
   1906 
   1907         assertStoredValues(Profile.CONTENT_URI, profileValues);
   1908     }
   1909 
   1910     private ContentValues[] getExpectedProfileDataValues() {
   1911         // Expected photo data values (only field is the photo BLOB, which we can't check).
   1912         ContentValues photoRow = new ContentValues();
   1913         photoRow.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
   1914 
   1915         // Expected phone data values.
   1916         ContentValues phoneRow = new ContentValues();
   1917         phoneRow.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1918         phoneRow.put(Phone.NUMBER, "18005554411");
   1919 
   1920         // Expected email data values.
   1921         ContentValues emailRow = new ContentValues();
   1922         emailRow.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   1923         emailRow.put(Email.ADDRESS, "mia.prophyl (at) acme.com");
   1924 
   1925         // Expected name data values.
   1926         ContentValues nameRow = new ContentValues();
   1927         nameRow.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
   1928         nameRow.put(StructuredName.DISPLAY_NAME, "Mia Prophyl");
   1929         nameRow.put(StructuredName.GIVEN_NAME, "Mia");
   1930         nameRow.put(StructuredName.FAMILY_NAME, "Prophyl");
   1931 
   1932         return new ContentValues[]{photoRow, phoneRow, emailRow, nameRow};
   1933     }
   1934 
   1935     public void testQueryProfileData() {
   1936         createBasicProfileContact(new ContentValues());
   1937 
   1938         assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
   1939                 getExpectedProfileDataValues());
   1940     }
   1941 
   1942     public void testQueryProfileEntities() {
   1943         createBasicProfileContact(new ContentValues());
   1944 
   1945         assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("entities").build(),
   1946                 getExpectedProfileDataValues());
   1947     }
   1948 
   1949     public void testQueryRawProfile() {
   1950         ContentValues profileValues = new ContentValues();
   1951         createBasicProfileContact(profileValues);
   1952 
   1953         // The raw contact view doesn't include the photo ID.
   1954         profileValues.remove(Contacts.PHOTO_ID);
   1955         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, profileValues);
   1956     }
   1957 
   1958     public void testQueryRawProfileById() {
   1959         ContentValues profileValues = new ContentValues();
   1960         long profileRawContactId = createBasicProfileContact(profileValues);
   1961 
   1962         // The raw contact view doesn't include the photo ID.
   1963         profileValues.remove(Contacts.PHOTO_ID);
   1964         assertStoredValues(ContentUris.withAppendedId(
   1965                 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId), profileValues);
   1966     }
   1967 
   1968     public void testQueryRawProfileData() {
   1969         long profileRawContactId = createBasicProfileContact(new ContentValues());
   1970 
   1971         assertStoredValues(ContentUris.withAppendedId(
   1972                 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
   1973                 .appendPath("data").build(), getExpectedProfileDataValues());
   1974     }
   1975 
   1976     public void testQueryRawProfileEntity() {
   1977         long profileRawContactId = createBasicProfileContact(new ContentValues());
   1978 
   1979         assertStoredValues(ContentUris.withAppendedId(
   1980                 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
   1981                 .appendPath("entity").build(), getExpectedProfileDataValues());
   1982     }
   1983 
   1984     public void testQueryDataForProfile() {
   1985         createBasicProfileContact(new ContentValues());
   1986 
   1987         assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
   1988                 getExpectedProfileDataValues());
   1989     }
   1990 
   1991     public void testUpdateProfileRawContact() {
   1992         createBasicProfileContact(new ContentValues());
   1993         ContentValues updatedValues = new ContentValues();
   1994         updatedValues.put(RawContacts.SEND_TO_VOICEMAIL, 0);
   1995         updatedValues.put(RawContacts.CUSTOM_RINGTONE, "rachmaninoff3");
   1996         updatedValues.put(RawContacts.STARRED, 1);
   1997         mResolver.update(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues, null, null);
   1998 
   1999         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues);
   2000     }
   2001 
   2002     public void testInsertProfileWithDataSetTriggersAccountCreation() {
   2003         // Check that we have no profile raw contacts.
   2004         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, new ContentValues[]{});
   2005 
   2006         // Insert a profile record with a new data set.
   2007         Account account = new Account("a", "b");
   2008         String dataSet = "c";
   2009         Uri profileUri = maybeAddAccountQueryParameters(Profile.CONTENT_RAW_CONTACTS_URI, account)
   2010                 .buildUpon().appendQueryParameter(RawContacts.DATA_SET, dataSet).build();
   2011         ContentValues values = new ContentValues();
   2012         long rawContactId = ContentUris.parseId(mResolver.insert(profileUri, values));
   2013         values.put(RawContacts._ID, rawContactId);
   2014 
   2015         // Check that querying for the profile gets the created raw contact.
   2016         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, values);
   2017     }
   2018 
   2019     public void testLoadProfilePhoto() throws IOException {
   2020         long rawContactId = createBasicProfileContact(new ContentValues());
   2021         insertPhoto(rawContactId, R.drawable.earth_normal);
   2022         assertInputStreamContent(
   2023                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.THUMBNAIL),
   2024                 Contacts.openContactPhotoInputStream(mResolver, Profile.CONTENT_URI, false));
   2025     }
   2026 
   2027     public void testLoadProfileDisplayPhoto() throws IOException {
   2028         long rawContactId = createBasicProfileContact(new ContentValues());
   2029         insertPhoto(rawContactId, R.drawable.earth_normal);
   2030         assertInputStreamContent(
   2031                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
   2032                 Contacts.openContactPhotoInputStream(mResolver, Profile.CONTENT_URI, true));
   2033     }
   2034 
   2035     public void testPhonesWithStatusUpdate() {
   2036 
   2037         ContentValues values = new ContentValues();
   2038         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   2039         long rawContactId = ContentUris.parseId(rawContactUri);
   2040         insertStructuredName(rawContactId, "John", "Doe");
   2041         Uri photoUri = insertPhoto(rawContactId);
   2042         long photoId = ContentUris.parseId(photoUri);
   2043         insertPhoneNumber(rawContactId, "18004664411");
   2044         insertPhoneNumber(rawContactId, "18004664412");
   2045         insertEmail(rawContactId, "goog411 (at) acme.com");
   2046         insertEmail(rawContactId, "goog412 (at) acme.com");
   2047 
   2048         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411 (at) acme.com",
   2049                 StatusUpdates.INVISIBLE, "Bad",
   2050                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   2051         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog412 (at) acme.com",
   2052                 StatusUpdates.AVAILABLE, "Good",
   2053                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VOICE);
   2054         long contactId = queryContactId(rawContactId);
   2055 
   2056         Uri uri = Data.CONTENT_URI;
   2057 
   2058         Cursor c = mResolver.query(uri, null, RawContacts.CONTACT_ID + "=" + contactId + " AND "
   2059                 + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'", null, Phone.NUMBER);
   2060         assertEquals(2, c.getCount());
   2061 
   2062         c.moveToFirst();
   2063 
   2064         values.clear();
   2065         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
   2066         values.put(Contacts.CONTACT_STATUS, "Bad");
   2067         values.put(Contacts.DISPLAY_NAME, "John Doe");
   2068         values.put(Phone.NUMBER, "18004664411");
   2069         values.putNull(Phone.LABEL);
   2070         values.put(RawContacts.CONTACT_ID, contactId);
   2071         assertCursorValues(c, values);
   2072 
   2073         c.moveToNext();
   2074 
   2075         values.clear();
   2076         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
   2077         values.put(Contacts.CONTACT_STATUS, "Bad");
   2078         values.put(Contacts.DISPLAY_NAME, "John Doe");
   2079         values.put(Phone.NUMBER, "18004664412");
   2080         values.putNull(Phone.LABEL);
   2081         values.put(RawContacts.CONTACT_ID, contactId);
   2082         assertCursorValues(c, values);
   2083 
   2084         c.close();
   2085     }
   2086 
   2087     public void testGroupQuery() {
   2088         Account account1 = new Account("a", "b");
   2089         Account account2 = new Account("c", "d");
   2090         long groupId1 = createGroup(account1, "e", "f");
   2091         long groupId2 = createGroup(account2, "g", "h");
   2092         Uri uri1 = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account1);
   2093         Uri uri2 = maybeAddAccountQueryParameters(Groups.CONTENT_URI, account2);
   2094         assertEquals(1, getCount(uri1, null, null));
   2095         assertEquals(1, getCount(uri2, null, null));
   2096         assertStoredValue(uri1, Groups._ID + "=" + groupId1, null, Groups._ID, groupId1) ;
   2097         assertStoredValue(uri2, Groups._ID + "=" + groupId2, null, Groups._ID, groupId2) ;
   2098     }
   2099 
   2100     public void testGroupInsert() {
   2101         ContentValues values = new ContentValues();
   2102 
   2103         values.put(Groups.ACCOUNT_NAME, "a");
   2104         values.put(Groups.ACCOUNT_TYPE, "b");
   2105         values.put(Groups.SOURCE_ID, "c");
   2106         values.put(Groups.VERSION, 42);
   2107         values.put(Groups.GROUP_VISIBLE, 1);
   2108         values.put(Groups.TITLE, "d");
   2109         values.put(Groups.TITLE_RES, 1234);
   2110         values.put(Groups.NOTES, "e");
   2111         values.put(Groups.RES_PACKAGE, "f");
   2112         values.put(Groups.SYSTEM_ID, "g");
   2113         values.put(Groups.DELETED, 1);
   2114         values.put(Groups.SYNC1, "h");
   2115         values.put(Groups.SYNC2, "i");
   2116         values.put(Groups.SYNC3, "j");
   2117         values.put(Groups.SYNC4, "k");
   2118 
   2119         Uri rowUri = mResolver.insert(Groups.CONTENT_URI, values);
   2120 
   2121         values.put(Groups.DIRTY, 1);
   2122         assertStoredValues(rowUri, values);
   2123     }
   2124 
   2125     public void testGroupCreationAfterMembershipInsert() {
   2126         long rawContactId1 = createRawContact(mAccount);
   2127         Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1");
   2128 
   2129         long groupId = assertSingleGroup(NO_LONG, mAccount, "gsid1", null);
   2130         assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
   2131                 rawContactId1, groupId, "gsid1");
   2132     }
   2133 
   2134     public void testGroupReuseAfterMembershipInsert() {
   2135         long rawContactId1 = createRawContact(mAccount);
   2136         long groupId1 = createGroup(mAccount, "gsid1", "title1");
   2137         Uri groupMembershipUri = insertGroupMembership(rawContactId1, "gsid1");
   2138 
   2139         assertSingleGroup(groupId1, mAccount, "gsid1", "title1");
   2140         assertSingleGroupMembership(ContentUris.parseId(groupMembershipUri),
   2141                 rawContactId1, groupId1, "gsid1");
   2142     }
   2143 
   2144     public void testGroupInsertFailureOnGroupIdConflict() {
   2145         long rawContactId1 = createRawContact(mAccount);
   2146         long groupId1 = createGroup(mAccount, "gsid1", "title1");
   2147 
   2148         ContentValues values = new ContentValues();
   2149         values.put(GroupMembership.RAW_CONTACT_ID, rawContactId1);
   2150         values.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
   2151         values.put(GroupMembership.GROUP_SOURCE_ID, "gsid1");
   2152         values.put(GroupMembership.GROUP_ROW_ID, groupId1);
   2153         try {
   2154             mResolver.insert(Data.CONTENT_URI, values);
   2155             fail("the insert was expected to fail, but it succeeded");
   2156         } catch (IllegalArgumentException e) {
   2157             // this was expected
   2158         }
   2159     }
   2160 
   2161     public void testGroupSummaryQuery() {
   2162         final Account account1 = new Account("accountName1", "accountType1");
   2163         final Account account2 = new Account("accountName2", "accountType2");
   2164         final long groupId1 = createGroup(account1, "sourceId1", "title1");
   2165         final long groupId2 = createGroup(account2, "sourceId2", "title2");
   2166         final long groupId3 = createGroup(account2, "sourceId3", "title3");
   2167 
   2168         // Prepare raw contact id not used at all, to test group summary uri won't be confused
   2169         // with it.
   2170         final long rawContactId0 = createRawContactWithName("firstName0", "lastName0");
   2171 
   2172         final long rawContactId1 = createRawContactWithName("firstName1", "lastName1");
   2173         insertEmail(rawContactId1, "address1 (at) email.com");
   2174         insertGroupMembership(rawContactId1, groupId1);
   2175 
   2176         final long rawContactId2 = createRawContactWithName("firstName2", "lastName2");
   2177         insertEmail(rawContactId2, "address2 (at) email.com");
   2178         insertPhoneNumber(rawContactId2, "222-222-2222");
   2179         insertGroupMembership(rawContactId2, groupId1);
   2180 
   2181         ContentValues v1 = new ContentValues();
   2182         v1.put(Groups._ID, groupId1);
   2183         v1.put(Groups.TITLE, "title1");
   2184         v1.put(Groups.SOURCE_ID, "sourceId1");
   2185         v1.put(Groups.ACCOUNT_NAME, account1.name);
   2186         v1.put(Groups.ACCOUNT_TYPE, account1.type);
   2187         v1.put(Groups.SUMMARY_COUNT, 2);
   2188         v1.put(Groups.SUMMARY_WITH_PHONES, 1);
   2189 
   2190         ContentValues v2 = new ContentValues();
   2191         v2.put(Groups._ID, groupId2);
   2192         v2.put(Groups.TITLE, "title2");
   2193         v2.put(Groups.SOURCE_ID, "sourceId2");
   2194         v2.put(Groups.ACCOUNT_NAME, account2.name);
   2195         v2.put(Groups.ACCOUNT_TYPE, account2.type);
   2196         v2.put(Groups.SUMMARY_COUNT, 0);
   2197         v2.put(Groups.SUMMARY_WITH_PHONES, 0);
   2198 
   2199         ContentValues v3 = new ContentValues();
   2200         v3.put(Groups._ID, groupId3);
   2201         v3.put(Groups.TITLE, "title3");
   2202         v3.put(Groups.SOURCE_ID, "sourceId3");
   2203         v3.put(Groups.ACCOUNT_NAME, account2.name);
   2204         v3.put(Groups.ACCOUNT_TYPE, account2.type);
   2205         v3.put(Groups.SUMMARY_COUNT, 0);
   2206         v3.put(Groups.SUMMARY_WITH_PHONES, 0);
   2207 
   2208         assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 });
   2209 
   2210         // Now rawContactId1 has two phone numbers.
   2211         insertPhoneNumber(rawContactId1, "111-111-1111");
   2212         insertPhoneNumber(rawContactId1, "111-111-1112");
   2213         // Result should reflect it correctly (don't count phone numbers but raw contacts)
   2214         v1.put(Groups.SUMMARY_WITH_PHONES, v1.getAsInteger(Groups.SUMMARY_WITH_PHONES) + 1);
   2215         assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 });
   2216 
   2217         // Introduce new raw contact, pretending the user added another info.
   2218         final long rawContactId3 = createRawContactWithName("firstName3", "lastName3");
   2219         insertEmail(rawContactId3, "address3 (at) email.com");
   2220         insertPhoneNumber(rawContactId3, "333-333-3333");
   2221         insertGroupMembership(rawContactId3, groupId2);
   2222         v2.put(Groups.SUMMARY_COUNT, v2.getAsInteger(Groups.SUMMARY_COUNT) + 1);
   2223         v2.put(Groups.SUMMARY_WITH_PHONES, v2.getAsInteger(Groups.SUMMARY_WITH_PHONES) + 1);
   2224 
   2225         assertStoredValues(Groups.CONTENT_SUMMARY_URI, new ContentValues[] { v1, v2, v3 });
   2226 
   2227         final Uri uri = Groups.CONTENT_SUMMARY_URI.buildUpon()
   2228                 .appendQueryParameter(Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT, "true")
   2229                 .build();
   2230         v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 1);
   2231         v2.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2);
   2232         v3.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 2);
   2233         assertStoredValues(uri, new ContentValues[] { v1, v2, v3 });
   2234 
   2235         // Introduce another group in account1, testing SUMMARY_GROUP_COUNT_PER_ACCOUNT correctly
   2236         // reflects the change.
   2237         final long groupId4 = createGroup(account1, "sourceId4", "title4");
   2238         v1.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
   2239                 v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT) + 1);
   2240         ContentValues v4 = new ContentValues();
   2241         v4.put(Groups._ID, groupId4);
   2242         v4.put(Groups.TITLE, "title4");
   2243         v4.put(Groups.SOURCE_ID, "sourceId4");
   2244         v4.put(Groups.ACCOUNT_NAME, account1.name);
   2245         v4.put(Groups.ACCOUNT_TYPE, account1.type);
   2246         v4.put(Groups.SUMMARY_COUNT, 0);
   2247         v4.put(Groups.SUMMARY_WITH_PHONES, 0);
   2248         v4.put(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
   2249                 v1.getAsInteger(Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT));
   2250         assertStoredValues(uri, new ContentValues[] { v1, v2, v3, v4 });
   2251 
   2252         // We change the tables dynamically according to the requested projection.
   2253         // Make sure the SUMMARY_COUNT column exists
   2254         v1.clear();
   2255         v1.put(Groups.SUMMARY_COUNT, 2);
   2256         v2.clear();
   2257         v2.put(Groups.SUMMARY_COUNT, 1);
   2258         v3.clear();
   2259         v3.put(Groups.SUMMARY_COUNT, 0);
   2260         v4.clear();
   2261         v4.put(Groups.SUMMARY_COUNT, 0);
   2262         assertStoredValuesWithProjection(uri, new ContentValues[] { v1, v2, v3, v4 });
   2263     }
   2264 
   2265     public void testSettingsQuery() {
   2266         Account account1 = new Account("a", "b");
   2267         Account account2 = new Account("c", "d");
   2268         AccountWithDataSet account3 = new AccountWithDataSet("e", "f", "plus");
   2269         createSettings(account1, "0", "0");
   2270         createSettings(account2, "1", "1");
   2271         createSettings(account3, "1", "0");
   2272         Uri uri1 = maybeAddAccountQueryParameters(Settings.CONTENT_URI, account1);
   2273         Uri uri2 = maybeAddAccountQueryParameters(Settings.CONTENT_URI, account2);
   2274         Uri uri3 = Settings.CONTENT_URI.buildUpon()
   2275                 .appendQueryParameter(RawContacts.ACCOUNT_NAME, account3.getAccountName())
   2276                 .appendQueryParameter(RawContacts.ACCOUNT_TYPE, account3.getAccountType())
   2277                 .appendQueryParameter(RawContacts.DATA_SET, account3.getDataSet())
   2278                 .build();
   2279         assertEquals(1, getCount(uri1, null, null));
   2280         assertEquals(1, getCount(uri2, null, null));
   2281         assertEquals(1, getCount(uri3, null, null));
   2282         assertStoredValue(uri1, Settings.SHOULD_SYNC, "0") ;
   2283         assertStoredValue(uri1, Settings.UNGROUPED_VISIBLE, "0");
   2284         assertStoredValue(uri2, Settings.SHOULD_SYNC, "1") ;
   2285         assertStoredValue(uri2, Settings.UNGROUPED_VISIBLE, "1");
   2286         assertStoredValue(uri3, Settings.SHOULD_SYNC, "1");
   2287         assertStoredValue(uri3, Settings.UNGROUPED_VISIBLE, "0");
   2288     }
   2289 
   2290     public void testSettingsInsertionPreventsDuplicates() {
   2291         Account account1 = new Account("a", "b");
   2292         AccountWithDataSet account2 = new AccountWithDataSet("c", "d", "plus");
   2293         createSettings(account1, "0", "0");
   2294         createSettings(account2, "1", "1");
   2295 
   2296         // Now try creating the settings rows again.  It should update the existing settings rows.
   2297         createSettings(account1, "1", "0");
   2298         assertStoredValue(Settings.CONTENT_URI,
   2299                 Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=?",
   2300                 new String[] {"a", "b"}, Settings.SHOULD_SYNC, "1");
   2301 
   2302         createSettings(account2, "0", "1");
   2303         assertStoredValue(Settings.CONTENT_URI,
   2304                 Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE + "=? AND " +
   2305                 Settings.DATA_SET + "=?",
   2306                 new String[] {"c", "d", "plus"}, Settings.SHOULD_SYNC, "0");
   2307     }
   2308 
   2309     public void testDisplayNameParsingWhenPartsUnspecified() {
   2310         long rawContactId = createRawContact();
   2311         ContentValues values = new ContentValues();
   2312         values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
   2313         insertStructuredName(rawContactId, values);
   2314 
   2315         assertStructuredName(rawContactId, "Mr.", "John", "Kevin", "von Smith", "Jr.");
   2316     }
   2317 
   2318     public void testDisplayNameParsingWhenPartsAreNull() {
   2319         long rawContactId = createRawContact();
   2320         ContentValues values = new ContentValues();
   2321         values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
   2322         values.putNull(StructuredName.GIVEN_NAME);
   2323         values.putNull(StructuredName.FAMILY_NAME);
   2324         insertStructuredName(rawContactId, values);
   2325         assertStructuredName(rawContactId, "Mr.", "John", "Kevin", "von Smith", "Jr.");
   2326     }
   2327 
   2328     public void testDisplayNameParsingWhenPartsSpecified() {
   2329         long rawContactId = createRawContact();
   2330         ContentValues values = new ContentValues();
   2331         values.put(StructuredName.DISPLAY_NAME, "Mr.John Kevin von Smith, Jr.");
   2332         values.put(StructuredName.FAMILY_NAME, "Johnson");
   2333         insertStructuredName(rawContactId, values);
   2334 
   2335         assertStructuredName(rawContactId, null, null, null, "Johnson", null);
   2336     }
   2337 
   2338     public void testContactWithoutPhoneticName() {
   2339         final long rawContactId = createRawContact(null);
   2340 
   2341         ContentValues values = new ContentValues();
   2342         values.put(StructuredName.PREFIX, "Mr");
   2343         values.put(StructuredName.GIVEN_NAME, "John");
   2344         values.put(StructuredName.MIDDLE_NAME, "K.");
   2345         values.put(StructuredName.FAMILY_NAME, "Doe");
   2346         values.put(StructuredName.SUFFIX, "Jr.");
   2347         Uri dataUri = insertStructuredName(rawContactId, values);
   2348 
   2349         values.clear();
   2350         values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
   2351         values.put(RawContacts.DISPLAY_NAME_PRIMARY, "Mr John K. Doe, Jr.");
   2352         values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "Mr Doe, John K., Jr.");
   2353         values.putNull(RawContacts.PHONETIC_NAME);
   2354         values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
   2355         values.put(RawContacts.SORT_KEY_PRIMARY, "John K. Doe, Jr.");
   2356         values.put(RawContacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr.");
   2357 
   2358         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
   2359         assertStoredValues(rawContactUri, values);
   2360 
   2361         values.clear();
   2362         values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
   2363         values.put(Contacts.DISPLAY_NAME_PRIMARY, "Mr John K. Doe, Jr.");
   2364         values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "Mr Doe, John K., Jr.");
   2365         values.putNull(Contacts.PHONETIC_NAME);
   2366         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
   2367         values.put(Contacts.SORT_KEY_PRIMARY, "John K. Doe, Jr.");
   2368         values.put(Contacts.SORT_KEY_ALTERNATIVE, "Doe, John K., Jr.");
   2369 
   2370         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
   2371                 queryContactId(rawContactId));
   2372         assertStoredValues(contactUri, values);
   2373 
   2374         // The same values should be available through a join with Data
   2375         assertStoredValues(dataUri, values);
   2376     }
   2377 
   2378     public void testContactWithChineseName() {
   2379 
   2380         // Only run this test when Chinese collation is supported
   2381         if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.CHINA)) {
   2382             return;
   2383         }
   2384 
   2385         long rawContactId = createRawContact(null);
   2386 
   2387         ContentValues values = new ContentValues();
   2388         values.put(StructuredName.DISPLAY_NAME, "\u6BB5\u5C0F\u6D9B");
   2389         Uri dataUri = insertStructuredName(rawContactId, values);
   2390 
   2391         values.clear();
   2392         values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
   2393         values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B");
   2394         values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
   2395         values.putNull(RawContacts.PHONETIC_NAME);
   2396         values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
   2397         values.put(RawContacts.SORT_KEY_PRIMARY, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
   2398         values.put(RawContacts.SORT_KEY_ALTERNATIVE, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
   2399 
   2400         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
   2401         assertStoredValues(rawContactUri, values);
   2402 
   2403         values.clear();
   2404         values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
   2405         values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u6BB5\u5C0F\u6D9B");
   2406         values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u6BB5\u5C0F\u6D9B");
   2407         values.putNull(Contacts.PHONETIC_NAME);
   2408         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
   2409         values.put(Contacts.SORT_KEY_PRIMARY, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
   2410         values.put(Contacts.SORT_KEY_ALTERNATIVE, "DUAN \u6BB5 XIAO \u5C0F TAO \u6D9B");
   2411 
   2412         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
   2413                 queryContactId(rawContactId));
   2414         assertStoredValues(contactUri, values);
   2415 
   2416         // The same values should be available through a join with Data
   2417         assertStoredValues(dataUri, values);
   2418     }
   2419 
   2420     public void testContactWithJapaneseName() {
   2421         long rawContactId = createRawContact(null);
   2422 
   2423         ContentValues values = new ContentValues();
   2424         values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77");
   2425         values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046");
   2426         Uri dataUri = insertStructuredName(rawContactId, values);
   2427 
   2428         values.clear();
   2429         values.put(RawContacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
   2430         values.put(RawContacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77");
   2431         values.put(RawContacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77");
   2432         values.put(RawContacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046");
   2433         values.put(RawContacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
   2434         values.put(RawContacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046");
   2435         values.put(RawContacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046");
   2436 
   2437         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
   2438         assertStoredValues(rawContactUri, values);
   2439 
   2440         values.clear();
   2441         values.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
   2442         values.put(Contacts.DISPLAY_NAME_PRIMARY, "\u7A7A\u6D77");
   2443         values.put(Contacts.DISPLAY_NAME_ALTERNATIVE, "\u7A7A\u6D77");
   2444         values.put(Contacts.PHONETIC_NAME, "\u304B\u3044\u304F\u3046");
   2445         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
   2446         values.put(Contacts.SORT_KEY_PRIMARY, "\u304B\u3044\u304F\u3046");
   2447         values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u304B\u3044\u304F\u3046");
   2448 
   2449         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
   2450                 queryContactId(rawContactId));
   2451         assertStoredValues(contactUri, values);
   2452 
   2453         // The same values should be available through a join with Data
   2454         assertStoredValues(dataUri, values);
   2455     }
   2456 
   2457     public void testDisplayNameUpdate() {
   2458         long rawContactId1 = createRawContact();
   2459         insertEmail(rawContactId1, "potato (at) acme.com", true);
   2460 
   2461         long rawContactId2 = createRawContact();
   2462         insertPhoneNumber(rawContactId2, "123456789", true);
   2463 
   2464         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
   2465                 rawContactId1, rawContactId2);
   2466 
   2467         assertAggregated(rawContactId1, rawContactId2, "123456789");
   2468 
   2469         insertStructuredName(rawContactId2, "Potato", "Head");
   2470 
   2471         assertAggregated(rawContactId1, rawContactId2, "Potato Head");
   2472         assertNetworkNotified(true);
   2473     }
   2474 
   2475     public void testDisplayNameFromData() {
   2476         long rawContactId = createRawContact();
   2477         long contactId = queryContactId(rawContactId);
   2478         ContentValues values = new ContentValues();
   2479 
   2480         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   2481 
   2482         assertStoredValue(uri, Contacts.DISPLAY_NAME, null);
   2483         insertEmail(rawContactId, "mike (at) monstersinc.com");
   2484         assertStoredValue(uri, Contacts.DISPLAY_NAME, "mike (at) monstersinc.com");
   2485 
   2486         insertEmail(rawContactId, "james (at) monstersinc.com", true);
   2487         assertStoredValue(uri, Contacts.DISPLAY_NAME, "james (at) monstersinc.com");
   2488 
   2489         insertPhoneNumber(rawContactId, "1-800-466-4411");
   2490         assertStoredValue(uri, Contacts.DISPLAY_NAME, "1-800-466-4411");
   2491 
   2492         // If there are title and company, the company is display name.
   2493         values.clear();
   2494         values.put(Organization.COMPANY, "Monsters Inc");
   2495         Uri organizationUri = insertOrganization(rawContactId, values);
   2496         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Monsters Inc");
   2497 
   2498         // If there is nickname, that is display name.
   2499         insertNickname(rawContactId, "Sully");
   2500         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Sully");
   2501 
   2502         // If there is structured name, that is display name.
   2503         values.clear();
   2504         values.put(StructuredName.GIVEN_NAME, "James");
   2505         values.put(StructuredName.MIDDLE_NAME, "P.");
   2506         values.put(StructuredName.FAMILY_NAME, "Sullivan");
   2507         insertStructuredName(rawContactId, values);
   2508         assertStoredValue(uri, Contacts.DISPLAY_NAME, "James P. Sullivan");
   2509     }
   2510 
   2511     public void testDisplayNameFromOrganizationWithoutPhoneticName() {
   2512         long rawContactId = createRawContact();
   2513         long contactId = queryContactId(rawContactId);
   2514         ContentValues values = new ContentValues();
   2515 
   2516         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   2517 
   2518         // If there is title without company, the title is display name.
   2519         values.clear();
   2520         values.put(Organization.TITLE, "Protagonist");
   2521         Uri organizationUri = insertOrganization(rawContactId, values);
   2522         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Protagonist");
   2523 
   2524         // If there are title and company, the company is display name.
   2525         values.clear();
   2526         values.put(Organization.COMPANY, "Monsters Inc");
   2527         mResolver.update(organizationUri, values, null, null);
   2528 
   2529         values.clear();
   2530         values.put(Contacts.DISPLAY_NAME, "Monsters Inc");
   2531         values.putNull(Contacts.PHONETIC_NAME);
   2532         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
   2533         values.put(Contacts.SORT_KEY_PRIMARY, "Monsters Inc");
   2534         values.put(Contacts.SORT_KEY_ALTERNATIVE, "Monsters Inc");
   2535         assertStoredValues(uri, values);
   2536     }
   2537 
   2538     public void testDisplayNameFromOrganizationWithJapanesePhoneticName() {
   2539         long rawContactId = createRawContact();
   2540         long contactId = queryContactId(rawContactId);
   2541         ContentValues values = new ContentValues();
   2542 
   2543         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   2544 
   2545         // If there is title without company, the title is display name.
   2546         values.clear();
   2547         values.put(Organization.COMPANY, "DoCoMo");
   2548         values.put(Organization.PHONETIC_NAME, "\u30C9\u30B3\u30E2");
   2549         Uri organizationUri = insertOrganization(rawContactId, values);
   2550 
   2551         values.clear();
   2552         values.put(Contacts.DISPLAY_NAME, "DoCoMo");
   2553         values.put(Contacts.PHONETIC_NAME, "\u30C9\u30B3\u30E2");
   2554         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.JAPANESE);
   2555         values.put(Contacts.SORT_KEY_PRIMARY, "\u30C9\u30B3\u30E2");
   2556         values.put(Contacts.SORT_KEY_ALTERNATIVE, "\u30C9\u30B3\u30E2");
   2557         assertStoredValues(uri, values);
   2558     }
   2559 
   2560     public void testDisplayNameFromOrganizationWithChineseName() {
   2561         boolean hasChineseCollator = false;
   2562         final Locale locale[] = Collator.getAvailableLocales();
   2563         for (int i = 0; i < locale.length; i++) {
   2564             if (locale[i].equals(Locale.CHINA)) {
   2565                 hasChineseCollator = true;
   2566                 break;
   2567             }
   2568         }
   2569 
   2570         if (!hasChineseCollator) {
   2571             return;
   2572         }
   2573 
   2574         long rawContactId = createRawContact();
   2575         long contactId = queryContactId(rawContactId);
   2576         ContentValues values = new ContentValues();
   2577 
   2578         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   2579 
   2580         // If there is title without company, the title is display name.
   2581         values.clear();
   2582         values.put(Organization.COMPANY, "\u4E2D\u56FD\u7535\u4FE1");
   2583         Uri organizationUri = insertOrganization(rawContactId, values);
   2584 
   2585         values.clear();
   2586         values.put(Contacts.DISPLAY_NAME, "\u4E2D\u56FD\u7535\u4FE1");
   2587         values.putNull(Contacts.PHONETIC_NAME);
   2588         values.put(Contacts.PHONETIC_NAME_STYLE, PhoneticNameStyle.UNDEFINED);
   2589         values.put(Contacts.SORT_KEY_PRIMARY, "ZHONG \u4E2D GUO \u56FD DIAN \u7535 XIN \u4FE1");
   2590         values.put(Contacts.SORT_KEY_ALTERNATIVE, "ZHONG \u4E2D GUO \u56FD DIAN \u7535 XIN \u4FE1");
   2591         assertStoredValues(uri, values);
   2592     }
   2593 
   2594     public void testLookupByOrganization() {
   2595         long rawContactId = createRawContact();
   2596         long contactId = queryContactId(rawContactId);
   2597         ContentValues values = new ContentValues();
   2598 
   2599         values.clear();
   2600         values.put(Organization.COMPANY, "acmecorp");
   2601         values.put(Organization.TITLE, "president");
   2602         Uri organizationUri = insertOrganization(rawContactId, values);
   2603 
   2604         assertContactFilter(contactId, "acmecorp");
   2605         assertContactFilter(contactId, "president");
   2606 
   2607         values.clear();
   2608         values.put(Organization.DEPARTMENT, "software");
   2609         mResolver.update(organizationUri, values, null, null);
   2610 
   2611         assertContactFilter(contactId, "acmecorp");
   2612         assertContactFilter(contactId, "president");
   2613 
   2614         values.clear();
   2615         values.put(Organization.COMPANY, "incredibles");
   2616         mResolver.update(organizationUri, values, null, null);
   2617 
   2618         assertContactFilter(contactId, "incredibles");
   2619         assertContactFilter(contactId, "president");
   2620 
   2621         values.clear();
   2622         values.put(Organization.TITLE, "director");
   2623         mResolver.update(organizationUri, values, null, null);
   2624 
   2625         assertContactFilter(contactId, "incredibles");
   2626         assertContactFilter(contactId, "director");
   2627 
   2628         values.clear();
   2629         values.put(Organization.COMPANY, "monsters");
   2630         values.put(Organization.TITLE, "scarer");
   2631         mResolver.update(organizationUri, values, null, null);
   2632 
   2633         assertContactFilter(contactId, "monsters");
   2634         assertContactFilter(contactId, "scarer");
   2635     }
   2636 
   2637     private void assertContactFilter(long contactId, String filter) {
   2638         Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
   2639         assertStoredValue(filterUri, Contacts._ID, contactId);
   2640     }
   2641 
   2642     private void assertContactFilterNoResult(String filter) {
   2643         Uri filterUri4 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, filter);
   2644         assertEquals(0, getCount(filterUri4, null, null));
   2645     }
   2646 
   2647     public void testSearchSnippetOrganization() throws Exception {
   2648         long rawContactId = createRawContactWithName();
   2649         long contactId = queryContactId(rawContactId);
   2650 
   2651         // Some random data element
   2652         insertEmail(rawContactId, "inc (at) corp.com");
   2653 
   2654         ContentValues values = new ContentValues();
   2655         values.clear();
   2656         values.put(Organization.COMPANY, "acmecorp");
   2657         values.put(Organization.TITLE, "engineer");
   2658         Uri organizationUri = insertOrganization(rawContactId, values);
   2659 
   2660         // Add another matching organization
   2661         values.put(Organization.COMPANY, "acmeinc");
   2662         insertOrganization(rawContactId, values);
   2663 
   2664         // Add another non-matching organization
   2665         values.put(Organization.COMPANY, "corpacme");
   2666         insertOrganization(rawContactId, values);
   2667 
   2668         // And another data element
   2669         insertEmail(rawContactId, "emca (at) corp.com", true, Email.TYPE_CUSTOM, "Custom");
   2670 
   2671         Uri filterUri = buildFilterUri("acme", true);
   2672 
   2673         values.clear();
   2674         values.put(Contacts._ID, contactId);
   2675         values.put(SearchSnippetColumns.SNIPPET, "engineer, [acmecorp]");
   2676         assertStoredValues(filterUri, values);
   2677     }
   2678 
   2679     public void testSearchSnippetEmail() throws Exception {
   2680         long rawContactId = createRawContact();
   2681         long contactId = queryContactId(rawContactId);
   2682         ContentValues values = new ContentValues();
   2683 
   2684         insertStructuredName(rawContactId, "John", "Doe");
   2685         Uri dataUri = insertEmail(rawContactId, "acme (at) corp.com", true, Email.TYPE_CUSTOM, "Custom");
   2686 
   2687         Uri filterUri = buildFilterUri("acme", true);
   2688 
   2689         values.clear();
   2690         values.put(Contacts._ID, contactId);
   2691         values.put(SearchSnippetColumns.SNIPPET, "[acme (at) corp.com]");
   2692         assertStoredValues(filterUri, values);
   2693     }
   2694 
   2695     public void testSearchSnippetPhone() throws Exception {
   2696         long rawContactId = createRawContact();
   2697         long contactId = queryContactId(rawContactId);
   2698         ContentValues values = new ContentValues();
   2699 
   2700         insertStructuredName(rawContactId, "Cave", "Johnson");
   2701         insertPhoneNumber(rawContactId, "(860) 555-1234");
   2702 
   2703         values.clear();
   2704         values.put(Contacts._ID, contactId);
   2705         values.put(SearchSnippetColumns.SNIPPET, "[(860) 555-1234]");
   2706 
   2707         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
   2708                 Uri.encode("86 (0) 5-55-12-34")), values);
   2709         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
   2710                 Uri.encode("860 555-1234")), values);
   2711         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
   2712                 Uri.encode("860")), values);
   2713         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
   2714                 Uri.encode("8605551234")), values);
   2715         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
   2716                 Uri.encode("860555")), values);
   2717         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
   2718                 Uri.encode("860 555")), values);
   2719         assertStoredValues(Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
   2720                 Uri.encode("860-555")), values);
   2721     }
   2722 
   2723     private Uri buildFilterUri(String query, boolean deferredSnippeting) {
   2724         Uri.Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon()
   2725                 .appendPath(Uri.encode(query));
   2726         if (deferredSnippeting) {
   2727             builder.appendQueryParameter(ContactsContract.DEFERRED_SNIPPETING, "1");
   2728         }
   2729         return builder.build();
   2730     }
   2731 
   2732     public void testSearchSnippetNickname() throws Exception {
   2733         long rawContactId = createRawContactWithName();
   2734         long contactId = queryContactId(rawContactId);
   2735         ContentValues values = new ContentValues();
   2736 
   2737         Uri dataUri = insertNickname(rawContactId, "Incredible");
   2738 
   2739         Uri filterUri = buildFilterUri("inc", true);
   2740 
   2741         values.clear();
   2742         values.put(Contacts._ID, contactId);
   2743         values.put(SearchSnippetColumns.SNIPPET, "[Incredible]");
   2744         assertStoredValues(filterUri, values);
   2745     }
   2746 
   2747     public void testSearchSnippetEmptyForNameInDisplayName() throws Exception {
   2748         long rawContactId = createRawContact();
   2749         long contactId = queryContactId(rawContactId);
   2750         insertStructuredName(rawContactId, "Cave", "Johnson");
   2751         insertEmail(rawContactId, "cave (at) aperturescience.com", true);
   2752 
   2753         ContentValues emptySnippet = new ContentValues();
   2754         emptySnippet.clear();
   2755         emptySnippet.put(Contacts._ID, contactId);
   2756         emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null);
   2757 
   2758         assertStoredValues(buildFilterUri("cave", true), emptySnippet);
   2759         assertStoredValues(buildFilterUri("john", true), emptySnippet);
   2760     }
   2761 
   2762     public void testSearchSnippetEmptyForNicknameInDisplayName() throws Exception {
   2763         long rawContactId = createRawContact();
   2764         long contactId = queryContactId(rawContactId);
   2765         insertNickname(rawContactId, "Caveman");
   2766         insertEmail(rawContactId, "cave (at) aperturescience.com", true);
   2767 
   2768         ContentValues emptySnippet = new ContentValues();
   2769         emptySnippet.clear();
   2770         emptySnippet.put(Contacts._ID, contactId);
   2771         emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null);
   2772 
   2773         assertStoredValues(buildFilterUri("cave", true), emptySnippet);
   2774     }
   2775 
   2776     public void testSearchSnippetEmptyForCompanyInDisplayName() throws Exception {
   2777         long rawContactId = createRawContact();
   2778         long contactId = queryContactId(rawContactId);
   2779         ContentValues company = new ContentValues();
   2780         company.clear();
   2781         company.put(Organization.COMPANY, "Aperture Science");
   2782         company.put(Organization.TITLE, "President");
   2783         insertOrganization(rawContactId, company);
   2784         insertEmail(rawContactId, "aperturepresident (at) aperturescience.com", true);
   2785 
   2786         ContentValues emptySnippet = new ContentValues();
   2787         emptySnippet.clear();
   2788         emptySnippet.put(Contacts._ID, contactId);
   2789         emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null);
   2790 
   2791         assertStoredValues(buildFilterUri("aperture", true), emptySnippet);
   2792     }
   2793 
   2794     public void testSearchSnippetEmptyForPhoneInDisplayName() throws Exception {
   2795         long rawContactId = createRawContact();
   2796         long contactId = queryContactId(rawContactId);
   2797         insertPhoneNumber(rawContactId, "860-555-1234");
   2798         insertEmail(rawContactId, "860 (at) aperturescience.com", true);
   2799 
   2800         ContentValues emptySnippet = new ContentValues();
   2801         emptySnippet.clear();
   2802         emptySnippet.put(Contacts._ID, contactId);
   2803         emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null);
   2804 
   2805         assertStoredValues(buildFilterUri("860", true), emptySnippet);
   2806     }
   2807 
   2808     public void testSearchSnippetEmptyForEmailInDisplayName() throws Exception {
   2809         long rawContactId = createRawContact();
   2810         long contactId = queryContactId(rawContactId);
   2811         insertEmail(rawContactId, "cave (at) aperturescience.com", true);
   2812         insertNote(rawContactId, "Cave Johnson is president of Aperture Science");
   2813 
   2814         ContentValues emptySnippet = new ContentValues();
   2815         emptySnippet.clear();
   2816         emptySnippet.put(Contacts._ID, contactId);
   2817         emptySnippet.put(SearchSnippetColumns.SNIPPET, (String) null);
   2818 
   2819         assertStoredValues(buildFilterUri("cave", true), emptySnippet);
   2820     }
   2821 
   2822     public void testDisplayNameUpdateFromStructuredNameUpdate() {
   2823         long rawContactId = createRawContact();
   2824         Uri nameUri = insertStructuredName(rawContactId, "Slinky", "Dog");
   2825 
   2826         long contactId = queryContactId(rawContactId);
   2827 
   2828         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   2829         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky Dog");
   2830 
   2831         ContentValues values = new ContentValues();
   2832         values.putNull(StructuredName.FAMILY_NAME);
   2833 
   2834         mResolver.update(nameUri, values, null, null);
   2835         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Slinky");
   2836 
   2837         values.putNull(StructuredName.GIVEN_NAME);
   2838 
   2839         mResolver.update(nameUri, values, null, null);
   2840         assertStoredValue(uri, Contacts.DISPLAY_NAME, null);
   2841 
   2842         values.put(StructuredName.FAMILY_NAME, "Dog");
   2843         mResolver.update(nameUri, values, null, null);
   2844 
   2845         assertStoredValue(uri, Contacts.DISPLAY_NAME, "Dog");
   2846     }
   2847 
   2848     public void testInsertDataWithContentProviderOperations() throws Exception {
   2849         ContentProviderOperation cpo1 = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
   2850                 .withValues(new ContentValues())
   2851                 .build();
   2852         ContentProviderOperation cpo2 = ContentProviderOperation.newInsert(Data.CONTENT_URI)
   2853                 .withValueBackReference(Data.RAW_CONTACT_ID, 0)
   2854                 .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
   2855                 .withValue(StructuredName.GIVEN_NAME, "John")
   2856                 .withValue(StructuredName.FAMILY_NAME, "Doe")
   2857                 .build();
   2858         ContentProviderResult[] results =
   2859                 mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(cpo1, cpo2));
   2860         long contactId = queryContactId(ContentUris.parseId(results[0].uri));
   2861         Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   2862         assertStoredValue(uri, Contacts.DISPLAY_NAME, "John Doe");
   2863     }
   2864 
   2865     public void testSendToVoicemailDefault() {
   2866         long rawContactId = createRawContactWithName();
   2867         long contactId = queryContactId(rawContactId);
   2868 
   2869         Cursor c = queryContact(contactId);
   2870         assertTrue(c.moveToNext());
   2871         int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL));
   2872         assertEquals(0, sendToVoicemail);
   2873         c.close();
   2874     }
   2875 
   2876     public void testSetSendToVoicemailAndRingtone() {
   2877         long rawContactId = createRawContactWithName();
   2878         long contactId = queryContactId(rawContactId);
   2879 
   2880         updateSendToVoicemailAndRingtone(contactId, true, "foo");
   2881         assertSendToVoicemailAndRingtone(contactId, true, "foo");
   2882         assertNetworkNotified(false);
   2883 
   2884         updateSendToVoicemailAndRingtoneWithSelection(contactId, false, "bar");
   2885         assertSendToVoicemailAndRingtone(contactId, false, "bar");
   2886         assertNetworkNotified(false);
   2887     }
   2888 
   2889     public void testSendToVoicemailAndRingtoneAfterAggregation() {
   2890         long rawContactId1 = createRawContactWithName("a", "b");
   2891         long contactId1 = queryContactId(rawContactId1);
   2892         updateSendToVoicemailAndRingtone(contactId1, true, "foo");
   2893 
   2894         long rawContactId2 = createRawContactWithName("c", "d");
   2895         long contactId2 = queryContactId(rawContactId2);
   2896         updateSendToVoicemailAndRingtone(contactId2, true, "bar");
   2897 
   2898         // Aggregate them
   2899         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
   2900                 rawContactId1, rawContactId2);
   2901 
   2902         // Both contacts had "send to VM", the contact now has the same value
   2903         assertSendToVoicemailAndRingtone(contactId1, true, "foo,bar"); // Either foo or bar
   2904     }
   2905 
   2906     public void testDoNotSendToVoicemailAfterAggregation() {
   2907         long rawContactId1 = createRawContactWithName("e", "f");
   2908         long contactId1 = queryContactId(rawContactId1);
   2909         updateSendToVoicemailAndRingtone(contactId1, true, null);
   2910 
   2911         long rawContactId2 = createRawContactWithName("g", "h");
   2912         long contactId2 = queryContactId(rawContactId2);
   2913         updateSendToVoicemailAndRingtone(contactId2, false, null);
   2914 
   2915         // Aggregate them
   2916         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
   2917                 rawContactId1, rawContactId2);
   2918 
   2919         // Since one of the contacts had "don't send to VM" that setting wins for the aggregate
   2920         assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), false, null);
   2921     }
   2922 
   2923     public void testSetSendToVoicemailAndRingtonePreservedAfterJoinAndSplit() {
   2924         long rawContactId1 = createRawContactWithName("i", "j");
   2925         long contactId1 = queryContactId(rawContactId1);
   2926         updateSendToVoicemailAndRingtone(contactId1, true, "foo");
   2927 
   2928         long rawContactId2 = createRawContactWithName("k", "l");
   2929         long contactId2 = queryContactId(rawContactId2);
   2930         updateSendToVoicemailAndRingtone(contactId2, false, "bar");
   2931 
   2932         // Aggregate them
   2933         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
   2934                 rawContactId1, rawContactId2);
   2935 
   2936         // Split them
   2937         setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
   2938                 rawContactId1, rawContactId2);
   2939 
   2940         assertSendToVoicemailAndRingtone(queryContactId(rawContactId1), true, "foo");
   2941         assertSendToVoicemailAndRingtone(queryContactId(rawContactId2), false, "bar");
   2942     }
   2943 
   2944     public void testStatusUpdateInsert() {
   2945         long rawContactId = createRawContact();
   2946         Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
   2947         long dataId = ContentUris.parseId(imUri);
   2948 
   2949         ContentValues values = new ContentValues();
   2950         values.put(StatusUpdates.DATA_ID, dataId);
   2951         values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM);
   2952         values.putNull(StatusUpdates.CUSTOM_PROTOCOL);
   2953         values.put(StatusUpdates.IM_HANDLE, "aim");
   2954         values.put(StatusUpdates.PRESENCE, StatusUpdates.INVISIBLE);
   2955         values.put(StatusUpdates.STATUS, "Hiding");
   2956         values.put(StatusUpdates.STATUS_TIMESTAMP, 100);
   2957         values.put(StatusUpdates.STATUS_RES_PACKAGE, "a.b.c");
   2958         values.put(StatusUpdates.STATUS_ICON, 1234);
   2959         values.put(StatusUpdates.STATUS_LABEL, 2345);
   2960 
   2961         Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values);
   2962 
   2963         assertStoredValues(resultUri, values);
   2964 
   2965         long contactId = queryContactId(rawContactId);
   2966         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   2967 
   2968         values.clear();
   2969         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
   2970         values.put(Contacts.CONTACT_STATUS, "Hiding");
   2971         values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100);
   2972         values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "a.b.c");
   2973         values.put(Contacts.CONTACT_STATUS_ICON, 1234);
   2974         values.put(Contacts.CONTACT_STATUS_LABEL, 2345);
   2975 
   2976         assertStoredValues(contactUri, values);
   2977 
   2978         values.clear();
   2979         values.put(StatusUpdates.DATA_ID, dataId);
   2980         values.put(StatusUpdates.STATUS, "Cloaked");
   2981         values.put(StatusUpdates.STATUS_TIMESTAMP, 200);
   2982         values.put(StatusUpdates.STATUS_RES_PACKAGE, "d.e.f");
   2983         values.put(StatusUpdates.STATUS_ICON, 4321);
   2984         values.put(StatusUpdates.STATUS_LABEL, 5432);
   2985         mResolver.insert(StatusUpdates.CONTENT_URI, values);
   2986 
   2987         values.clear();
   2988         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
   2989         values.put(Contacts.CONTACT_STATUS, "Cloaked");
   2990         values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 200);
   2991         values.put(Contacts.CONTACT_STATUS_RES_PACKAGE, "d.e.f");
   2992         values.put(Contacts.CONTACT_STATUS_ICON, 4321);
   2993         values.put(Contacts.CONTACT_STATUS_LABEL, 5432);
   2994         assertStoredValues(contactUri, values);
   2995     }
   2996 
   2997     public void testStatusUpdateInferAttribution() {
   2998         long rawContactId = createRawContact();
   2999         Uri imUri = insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
   3000         long dataId = ContentUris.parseId(imUri);
   3001 
   3002         ContentValues values = new ContentValues();
   3003         values.put(StatusUpdates.DATA_ID, dataId);
   3004         values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_AIM);
   3005         values.put(StatusUpdates.IM_HANDLE, "aim");
   3006         values.put(StatusUpdates.STATUS, "Hiding");
   3007 
   3008         Uri resultUri = mResolver.insert(StatusUpdates.CONTENT_URI, values);
   3009 
   3010         values.clear();
   3011         values.put(StatusUpdates.DATA_ID, dataId);
   3012         values.put(StatusUpdates.STATUS_LABEL, com.android.internal.R.string.imProtocolAim);
   3013         values.put(StatusUpdates.STATUS, "Hiding");
   3014 
   3015         assertStoredValues(resultUri, values);
   3016     }
   3017 
   3018     public void testStatusUpdateMatchingImOrEmail() {
   3019         long rawContactId = createRawContact();
   3020         insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
   3021         insertImHandle(rawContactId, Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im");
   3022         insertEmail(rawContactId, "m (at) acme.com");
   3023 
   3024         // Match on IM (standard)
   3025         insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
   3026                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   3027 
   3028         // Match on IM (custom)
   3029         insertStatusUpdate(Im.PROTOCOL_CUSTOM, "my_im_proto", "my_im", StatusUpdates.IDLE, "Idle",
   3030                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
   3031 
   3032         // Match on Email
   3033         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "m (at) acme.com", StatusUpdates.AWAY, "Away",
   3034                 StatusUpdates.CAPABILITY_HAS_VOICE);
   3035 
   3036         // No match
   3037         insertStatusUpdate(Im.PROTOCOL_ICQ, null, "12345", StatusUpdates.DO_NOT_DISTURB, "Go away",
   3038                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   3039 
   3040         Cursor c = mResolver.query(StatusUpdates.CONTENT_URI, new String[] {
   3041                 StatusUpdates.DATA_ID, StatusUpdates.PROTOCOL, StatusUpdates.CUSTOM_PROTOCOL,
   3042                 StatusUpdates.PRESENCE, StatusUpdates.STATUS},
   3043                 PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null, StatusUpdates.DATA_ID);
   3044         assertTrue(c.moveToNext());
   3045         assertStatusUpdate(c, Im.PROTOCOL_AIM, null, StatusUpdates.AVAILABLE, "Available");
   3046         assertTrue(c.moveToNext());
   3047         assertStatusUpdate(c, Im.PROTOCOL_CUSTOM, "my_im_proto", StatusUpdates.IDLE, "Idle");
   3048         assertTrue(c.moveToNext());
   3049         assertStatusUpdate(c, Im.PROTOCOL_GOOGLE_TALK, null, StatusUpdates.AWAY, "Away");
   3050         assertFalse(c.moveToNext());
   3051         c.close();
   3052 
   3053         long contactId = queryContactId(rawContactId);
   3054         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   3055 
   3056         ContentValues values = new ContentValues();
   3057         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
   3058         values.put(Contacts.CONTACT_STATUS, "Available");
   3059         assertStoredValuesWithProjection(contactUri, values);
   3060     }
   3061 
   3062     public void testStatusUpdateUpdateAndDelete() {
   3063         long rawContactId = createRawContact();
   3064         insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
   3065 
   3066         long contactId = queryContactId(rawContactId);
   3067         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   3068 
   3069         ContentValues values = new ContentValues();
   3070         values.putNull(Contacts.CONTACT_PRESENCE);
   3071         values.putNull(Contacts.CONTACT_STATUS);
   3072         assertStoredValuesWithProjection(contactUri, values);
   3073 
   3074         insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AWAY, "BUSY",
   3075                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   3076         insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.DO_NOT_DISTURB, "GO AWAY",
   3077                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   3078         Uri statusUri =
   3079             insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
   3080                     StatusUpdates.CAPABILITY_HAS_CAMERA);
   3081         long statusId = ContentUris.parseId(statusUri);
   3082 
   3083         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
   3084         values.put(Contacts.CONTACT_STATUS, "Available");
   3085         assertStoredValuesWithProjection(contactUri, values);
   3086 
   3087         // update status_updates table to set new values for
   3088         //     status_updates.status
   3089         //     status_updates.status_ts
   3090         //     presence
   3091         long updatedTs = 200;
   3092         String testUpdate = "test_update";
   3093         String selection = StatusUpdates.DATA_ID + "=" + statusId;
   3094         values.clear();
   3095         values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
   3096         values.put(StatusUpdates.STATUS, testUpdate);
   3097         values.put(StatusUpdates.PRESENCE, "presence_test");
   3098         mResolver.update(StatusUpdates.CONTENT_URI, values,
   3099                 StatusUpdates.DATA_ID + "=" + statusId, null);
   3100         assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
   3101 
   3102         // update status_updates table to set new values for columns in status_updates table ONLY
   3103         // i.e., no rows in presence table are to be updated.
   3104         updatedTs = 300;
   3105         testUpdate = "test_update_new";
   3106         selection = StatusUpdates.DATA_ID + "=" + statusId;
   3107         values.clear();
   3108         values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
   3109         values.put(StatusUpdates.STATUS, testUpdate);
   3110         mResolver.update(StatusUpdates.CONTENT_URI, values,
   3111                 StatusUpdates.DATA_ID + "=" + statusId, null);
   3112         // make sure the presence column value is still the old value
   3113         values.put(StatusUpdates.PRESENCE, "presence_test");
   3114         assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
   3115 
   3116         // update status_updates table to set new values for columns in presence table ONLY
   3117         // i.e., no rows in status_updates table are to be updated.
   3118         selection = StatusUpdates.DATA_ID + "=" + statusId;
   3119         values.clear();
   3120         values.put(StatusUpdates.PRESENCE, "presence_test_new");
   3121         mResolver.update(StatusUpdates.CONTENT_URI, values,
   3122                 StatusUpdates.DATA_ID + "=" + statusId, null);
   3123         // make sure the status_updates table is not updated
   3124         values.put(StatusUpdates.STATUS_TIMESTAMP, updatedTs);
   3125         values.put(StatusUpdates.STATUS, testUpdate);
   3126         assertStoredValuesWithProjection(StatusUpdates.CONTENT_URI, values);
   3127 
   3128         // effect "delete status_updates" operation and expect the following
   3129         //   data deleted from status_updates table
   3130         //   presence set to null
   3131         mResolver.delete(StatusUpdates.CONTENT_URI, StatusUpdates.DATA_ID + "=" + statusId, null);
   3132         values.clear();
   3133         values.putNull(Contacts.CONTACT_PRESENCE);
   3134         assertStoredValuesWithProjection(contactUri, values);
   3135     }
   3136 
   3137     public void testStatusUpdateUpdateToNull() {
   3138         long rawContactId = createRawContact();
   3139         insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
   3140 
   3141         long contactId = queryContactId(rawContactId);
   3142         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   3143 
   3144         ContentValues values = new ContentValues();
   3145         Uri statusUri =
   3146             insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", StatusUpdates.AVAILABLE, "Available",
   3147                     StatusUpdates.CAPABILITY_HAS_CAMERA);
   3148         long statusId = ContentUris.parseId(statusUri);
   3149 
   3150         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.AVAILABLE);
   3151         values.put(Contacts.CONTACT_STATUS, "Available");
   3152         assertStoredValuesWithProjection(contactUri, values);
   3153 
   3154         values.clear();
   3155         values.putNull(StatusUpdates.PRESENCE);
   3156         mResolver.update(StatusUpdates.CONTENT_URI, values,
   3157                 StatusUpdates.DATA_ID + "=" + statusId, null);
   3158 
   3159         values.clear();
   3160         values.putNull(Contacts.CONTACT_PRESENCE);
   3161         values.put(Contacts.CONTACT_STATUS, "Available");
   3162         assertStoredValuesWithProjection(contactUri, values);
   3163     }
   3164 
   3165     public void testStatusUpdateWithTimestamp() {
   3166         long rawContactId = createRawContact();
   3167         insertImHandle(rawContactId, Im.PROTOCOL_AIM, null, "aim");
   3168         insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
   3169 
   3170         long contactId = queryContactId(rawContactId);
   3171         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   3172         insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Offline", 80,
   3173                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
   3174         insertStatusUpdate(Im.PROTOCOL_AIM, null, "aim", 0, "Available", 100,
   3175                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
   3176         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", 0, "Busy", 90,
   3177                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
   3178 
   3179         // Should return the latest status
   3180         ContentValues values = new ContentValues();
   3181         values.put(Contacts.CONTACT_STATUS_TIMESTAMP, 100);
   3182         values.put(Contacts.CONTACT_STATUS, "Available");
   3183         assertStoredValuesWithProjection(contactUri, values);
   3184     }
   3185 
   3186     private void assertStatusUpdate(Cursor c, int protocol, String customProtocol, int presence,
   3187             String status) {
   3188         ContentValues values = new ContentValues();
   3189         values.put(StatusUpdates.PROTOCOL, protocol);
   3190         values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
   3191         values.put(StatusUpdates.PRESENCE, presence);
   3192         values.put(StatusUpdates.STATUS, status);
   3193         assertCursorValues(c, values);
   3194     }
   3195 
   3196     // Stream item query test cases.
   3197 
   3198     public void testQueryStreamItemsByRawContactId() {
   3199         long rawContactId = createRawContact(mAccount);
   3200         ContentValues values = buildGenericStreamItemValues();
   3201         insertStreamItem(rawContactId, values, mAccount);
   3202         assertStoredValues(
   3203                 Uri.withAppendedPath(
   3204                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3205                         RawContacts.StreamItems.CONTENT_DIRECTORY),
   3206                 values);
   3207     }
   3208 
   3209     public void testQueryStreamItemsByContactId() {
   3210         long rawContactId = createRawContact();
   3211         long contactId = queryContactId(rawContactId);
   3212         ContentValues values = buildGenericStreamItemValues();
   3213         insertStreamItem(rawContactId, values, null);
   3214         assertStoredValues(
   3215                 Uri.withAppendedPath(
   3216                         ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   3217                         Contacts.StreamItems.CONTENT_DIRECTORY),
   3218                 values);
   3219     }
   3220 
   3221     public void testQueryStreamItemsByLookupKey() {
   3222         long rawContactId = createRawContact();
   3223         long contactId = queryContactId(rawContactId);
   3224         String lookupKey = queryLookupKey(contactId);
   3225         ContentValues values = buildGenericStreamItemValues();
   3226         insertStreamItem(rawContactId, values, null);
   3227         assertStoredValues(
   3228                 Uri.withAppendedPath(
   3229                         Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
   3230                         Contacts.StreamItems.CONTENT_DIRECTORY),
   3231                 values);
   3232     }
   3233 
   3234     public void testQueryStreamItemsByLookupKeyAndContactId() {
   3235         long rawContactId = createRawContact();
   3236         long contactId = queryContactId(rawContactId);
   3237         String lookupKey = queryLookupKey(contactId);
   3238         ContentValues values = buildGenericStreamItemValues();
   3239         insertStreamItem(rawContactId, values, null);
   3240         assertStoredValues(
   3241                 Uri.withAppendedPath(
   3242                         ContentUris.withAppendedId(
   3243                                 Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
   3244                                 contactId),
   3245                         Contacts.StreamItems.CONTENT_DIRECTORY),
   3246                 values);
   3247     }
   3248 
   3249     public void testQueryStreamItems() {
   3250         long rawContactId = createRawContact();
   3251         ContentValues values = buildGenericStreamItemValues();
   3252         insertStreamItem(rawContactId, values, null);
   3253         assertStoredValues(StreamItems.CONTENT_URI, values);
   3254     }
   3255 
   3256     public void testQueryStreamItemsWithSelection() {
   3257         long rawContactId = createRawContact();
   3258         ContentValues firstValues = buildGenericStreamItemValues();
   3259         insertStreamItem(rawContactId, firstValues, null);
   3260 
   3261         ContentValues secondValues = buildGenericStreamItemValues();
   3262         secondValues.put(StreamItems.TEXT, "Goodbye world");
   3263         insertStreamItem(rawContactId, secondValues, null);
   3264 
   3265         // Select only the first stream item.
   3266         assertStoredValues(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?",
   3267                 new String[]{"Hello world"}, firstValues);
   3268 
   3269         // Select only the second stream item.
   3270         assertStoredValues(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?",
   3271                 new String[]{"Goodbye world"}, secondValues);
   3272     }
   3273 
   3274     public void testQueryStreamItemById() {
   3275         long rawContactId = createRawContact();
   3276         ContentValues firstValues = buildGenericStreamItemValues();
   3277         Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
   3278         long firstStreamItemId = ContentUris.parseId(resultUri);
   3279 
   3280         ContentValues secondValues = buildGenericStreamItemValues();
   3281         secondValues.put(StreamItems.TEXT, "Goodbye world");
   3282         resultUri = insertStreamItem(rawContactId, secondValues, null);
   3283         long secondStreamItemId = ContentUris.parseId(resultUri);
   3284 
   3285         // Select only the first stream item.
   3286         assertStoredValues(ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId),
   3287                 firstValues);
   3288 
   3289         // Select only the second stream item.
   3290         assertStoredValues(ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId),
   3291                 secondValues);
   3292     }
   3293 
   3294     // Stream item photo insertion + query test cases.
   3295 
   3296     public void testQueryStreamItemPhotoWithSelection() {
   3297         long rawContactId = createRawContact();
   3298         ContentValues values = buildGenericStreamItemValues();
   3299         Uri resultUri = insertStreamItem(rawContactId, values, null);
   3300         long streamItemId = ContentUris.parseId(resultUri);
   3301 
   3302         ContentValues photo1Values = buildGenericStreamItemPhotoValues(1);
   3303         insertStreamItemPhoto(streamItemId, photo1Values, null);
   3304         photo1Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
   3305         ContentValues photo2Values = buildGenericStreamItemPhotoValues(2);
   3306         insertStreamItemPhoto(streamItemId, photo2Values, null);
   3307 
   3308         // Select only the first photo.
   3309         assertStoredValues(StreamItems.CONTENT_PHOTO_URI, StreamItemPhotos.SORT_INDEX + "=?",
   3310                 new String[]{"1"}, photo1Values);
   3311     }
   3312 
   3313     public void testQueryStreamItemPhotoByStreamItemId() {
   3314         long rawContactId = createRawContact();
   3315 
   3316         // Insert a first stream item.
   3317         ContentValues firstValues = buildGenericStreamItemValues();
   3318         Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
   3319         long firstStreamItemId = ContentUris.parseId(resultUri);
   3320 
   3321         // Insert a second stream item.
   3322         ContentValues secondValues = buildGenericStreamItemValues();
   3323         resultUri = insertStreamItem(rawContactId, secondValues, null);
   3324         long secondStreamItemId = ContentUris.parseId(resultUri);
   3325 
   3326         // Add a photo to the first stream item.
   3327         ContentValues photo1Values = buildGenericStreamItemPhotoValues(1);
   3328         insertStreamItemPhoto(firstStreamItemId, photo1Values, null);
   3329         photo1Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
   3330 
   3331         // Add a photo to the second stream item.
   3332         ContentValues photo2Values = buildGenericStreamItemPhotoValues(1);
   3333         photo2Values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
   3334                 R.drawable.nebula, PhotoSize.ORIGINAL));
   3335         insertStreamItemPhoto(secondStreamItemId, photo2Values, null);
   3336         photo2Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
   3337 
   3338         // Select only the photos from the second stream item.
   3339         assertStoredValues(Uri.withAppendedPath(
   3340                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId),
   3341                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), photo2Values);
   3342     }
   3343 
   3344     public void testQueryStreamItemPhotoByStreamItemPhotoId() {
   3345         long rawContactId = createRawContact();
   3346 
   3347         // Insert a first stream item.
   3348         ContentValues firstValues = buildGenericStreamItemValues();
   3349         Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
   3350         long firstStreamItemId = ContentUris.parseId(resultUri);
   3351 
   3352         // Insert a second stream item.
   3353         ContentValues secondValues = buildGenericStreamItemValues();
   3354         resultUri = insertStreamItem(rawContactId, secondValues, null);
   3355         long secondStreamItemId = ContentUris.parseId(resultUri);
   3356 
   3357         // Add a photo to the first stream item.
   3358         ContentValues photo1Values = buildGenericStreamItemPhotoValues(1);
   3359         resultUri = insertStreamItemPhoto(firstStreamItemId, photo1Values, null);
   3360         long firstPhotoId = ContentUris.parseId(resultUri);
   3361         photo1Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
   3362 
   3363         // Add a photo to the second stream item.
   3364         ContentValues photo2Values = buildGenericStreamItemPhotoValues(1);
   3365         photo2Values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
   3366                 R.drawable.galaxy, PhotoSize.ORIGINAL));
   3367         resultUri = insertStreamItemPhoto(secondStreamItemId, photo2Values, null);
   3368         long secondPhotoId = ContentUris.parseId(resultUri);
   3369         photo2Values.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
   3370 
   3371         // Select the first photo.
   3372         assertStoredValues(ContentUris.withAppendedId(
   3373                 Uri.withAppendedPath(
   3374                         ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId),
   3375                         StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
   3376                 firstPhotoId),
   3377                 photo1Values);
   3378 
   3379         // Select the second photo.
   3380         assertStoredValues(ContentUris.withAppendedId(
   3381                 Uri.withAppendedPath(
   3382                         ContentUris.withAppendedId(StreamItems.CONTENT_URI, secondStreamItemId),
   3383                         StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
   3384                 secondPhotoId),
   3385                 photo2Values);
   3386     }
   3387 
   3388     // Stream item insertion test cases.
   3389 
   3390     public void testInsertStreamItemIntoOtherAccount() {
   3391         long rawContactId = createRawContact(mAccount);
   3392         ContentValues values = buildGenericStreamItemValues();
   3393         try {
   3394             insertStreamItem(rawContactId, values, mAccountTwo);
   3395             fail("Stream insertion was allowed in another account's raw contact.");
   3396         } catch (SecurityException expected) {
   3397             // Trying to insert stream items into account one's raw contact is forbidden.
   3398         }
   3399     }
   3400 
   3401     public void testInsertStreamItemInProfileRequiresWriteProfileAccess() {
   3402         long profileRawContactId = createBasicProfileContact(new ContentValues());
   3403 
   3404         // With our (default) write profile permission, we should be able to insert a stream item.
   3405         ContentValues values = buildGenericStreamItemValues();
   3406         insertStreamItem(profileRawContactId, values, null);
   3407 
   3408         // Now take away write profile permission.
   3409         mActor.removePermissions("android.permission.WRITE_PROFILE");
   3410 
   3411         // Try inserting another stream item.
   3412         try {
   3413             insertStreamItem(profileRawContactId, values, null);
   3414             fail("Should require WRITE_PROFILE access to insert a stream item in the profile.");
   3415         } catch (SecurityException expected) {
   3416             // Trying to insert a stream item in the profile without WRITE_PROFILE permission
   3417             // should fail.
   3418         }
   3419     }
   3420 
   3421     public void testInsertStreamItemWithContentValues() {
   3422         long rawContactId = createRawContact();
   3423         ContentValues values = buildGenericStreamItemValues();
   3424         values.put(StreamItems.RAW_CONTACT_ID, rawContactId);
   3425         mResolver.insert(StreamItems.CONTENT_URI, values);
   3426         assertStoredValues(Uri.withAppendedPath(
   3427                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3428                 RawContacts.StreamItems.CONTENT_DIRECTORY), values);
   3429     }
   3430 
   3431     public void testInsertStreamItemOverLimit() {
   3432         long rawContactId = createRawContact();
   3433         ContentValues values = buildGenericStreamItemValues();
   3434         values.put(StreamItems.RAW_CONTACT_ID, rawContactId);
   3435 
   3436         List<Long> streamItemIds = Lists.newArrayList();
   3437 
   3438         // Insert MAX + 1 stream items.
   3439         long baseTime = System.currentTimeMillis();
   3440         for (int i = 0; i < 6; i++) {
   3441             values.put(StreamItems.TIMESTAMP, baseTime + i);
   3442             Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values);
   3443             streamItemIds.add(ContentUris.parseId(resultUri));
   3444         }
   3445         Long doomedStreamItemId = streamItemIds.get(0);
   3446 
   3447         // There should only be MAX items.  The oldest one should have been cleaned up.
   3448         Cursor c = mResolver.query(
   3449                 Uri.withAppendedPath(
   3450                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3451                         RawContacts.StreamItems.CONTENT_DIRECTORY),
   3452                 new String[]{StreamItems._ID}, null, null, null);
   3453         try {
   3454             while(c.moveToNext()) {
   3455                 long streamItemId = c.getLong(0);
   3456                 streamItemIds.remove(streamItemId);
   3457             }
   3458         } finally {
   3459             c.close();
   3460         }
   3461 
   3462         assertEquals(1, streamItemIds.size());
   3463         assertEquals(doomedStreamItemId, streamItemIds.get(0));
   3464     }
   3465 
   3466     public void testInsertStreamItemOlderThanOldestInLimit() {
   3467         long rawContactId = createRawContact();
   3468         ContentValues values = buildGenericStreamItemValues();
   3469         values.put(StreamItems.RAW_CONTACT_ID, rawContactId);
   3470 
   3471         // Insert MAX stream items.
   3472         long baseTime = System.currentTimeMillis();
   3473         for (int i = 0; i < 5; i++) {
   3474             values.put(StreamItems.TIMESTAMP, baseTime + i);
   3475             Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values);
   3476             assertNotSame("Expected non-0 stream item ID to be inserted",
   3477                     0L, ContentUris.parseId(resultUri));
   3478         }
   3479 
   3480         // Now try to insert a stream item that's older.  It should be deleted immediately
   3481         // and return an ID of 0.
   3482         values.put(StreamItems.TIMESTAMP, baseTime - 1);
   3483         Uri resultUri = mResolver.insert(StreamItems.CONTENT_URI, values);
   3484         assertEquals(0L, ContentUris.parseId(resultUri));
   3485     }
   3486 
   3487     // Stream item photo insertion test cases.
   3488 
   3489     public void testInsertStreamItemsAndPhotosInBatch() throws Exception {
   3490         long rawContactId = createRawContact();
   3491         ContentValues streamItemValues = buildGenericStreamItemValues();
   3492         ContentValues streamItemPhotoValues = buildGenericStreamItemPhotoValues(0);
   3493 
   3494         ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
   3495         ops.add(ContentProviderOperation.newInsert(
   3496                 Uri.withAppendedPath(
   3497                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3498                         RawContacts.StreamItems.CONTENT_DIRECTORY))
   3499                 .withValues(streamItemValues).build());
   3500         for (int i = 0; i < 5; i++) {
   3501             streamItemPhotoValues.put(StreamItemPhotos.SORT_INDEX, i);
   3502             ops.add(ContentProviderOperation.newInsert(StreamItems.CONTENT_PHOTO_URI)
   3503                     .withValues(streamItemPhotoValues)
   3504                     .withValueBackReference(StreamItemPhotos.STREAM_ITEM_ID, 0)
   3505                     .build());
   3506         }
   3507         mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
   3508 
   3509         // Check that all five photos were inserted under the raw contact.
   3510         Cursor c = mResolver.query(StreamItems.CONTENT_URI, new String[]{StreamItems._ID},
   3511                 StreamItems.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(rawContactId)},
   3512                 null);
   3513         long streamItemId = 0;
   3514         try {
   3515             assertEquals(1, c.getCount());
   3516             c.moveToFirst();
   3517             streamItemId = c.getLong(0);
   3518         } finally {
   3519             c.close();
   3520         }
   3521 
   3522         c = mResolver.query(Uri.withAppendedPath(
   3523                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3524                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
   3525                 new String[]{StreamItemPhotos._ID, StreamItemPhotos.PHOTO_URI},
   3526                 null, null, null);
   3527         try {
   3528             assertEquals(5, c.getCount());
   3529             byte[] expectedPhotoBytes = loadPhotoFromResource(
   3530                     R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO);
   3531             while (c.moveToNext()) {
   3532                 String photoUri = c.getString(1);
   3533                 assertInputStreamContent(expectedPhotoBytes,
   3534                         mResolver.openInputStream(Uri.parse(photoUri)));
   3535             }
   3536         } finally {
   3537             c.close();
   3538         }
   3539     }
   3540 
   3541     // Stream item update test cases.
   3542 
   3543     public void testUpdateStreamItemById() {
   3544         long rawContactId = createRawContact();
   3545         ContentValues values = buildGenericStreamItemValues();
   3546         Uri resultUri = insertStreamItem(rawContactId, values, null);
   3547         long streamItemId = ContentUris.parseId(resultUri);
   3548         values.put(StreamItems.TEXT, "Goodbye world");
   3549         mResolver.update(ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId), values,
   3550                 null, null);
   3551         assertStoredValues(Uri.withAppendedPath(
   3552                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3553                 RawContacts.StreamItems.CONTENT_DIRECTORY), values);
   3554     }
   3555 
   3556     public void testUpdateStreamItemWithContentValues() {
   3557         long rawContactId = createRawContact();
   3558         ContentValues values = buildGenericStreamItemValues();
   3559         Uri resultUri = insertStreamItem(rawContactId, values, null);
   3560         long streamItemId = ContentUris.parseId(resultUri);
   3561         values.put(StreamItems._ID, streamItemId);
   3562         values.put(StreamItems.TEXT, "Goodbye world");
   3563         mResolver.update(StreamItems.CONTENT_URI, values, null, null);
   3564         assertStoredValues(Uri.withAppendedPath(
   3565                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3566                 RawContacts.StreamItems.CONTENT_DIRECTORY), values);
   3567     }
   3568 
   3569     public void testUpdateStreamItemFromOtherAccount() {
   3570         long rawContactId = createRawContact(mAccount);
   3571         ContentValues values = buildGenericStreamItemValues();
   3572         Uri resultUri = insertStreamItem(rawContactId, values, mAccount);
   3573         long streamItemId = ContentUris.parseId(resultUri);
   3574         values.put(StreamItems._ID, streamItemId);
   3575         values.put(StreamItems.TEXT, "Goodbye world");
   3576         try {
   3577             mResolver.update(maybeAddAccountQueryParameters(StreamItems.CONTENT_URI, mAccountTwo),
   3578                     values, null, null);
   3579             fail("Should not be able to update stream items inserted by another account");
   3580         } catch (SecurityException expected) {
   3581             // Can't update the stream items from another account.
   3582         }
   3583     }
   3584 
   3585     // Stream item photo update test cases.
   3586 
   3587     public void testUpdateStreamItemPhotoById() throws IOException {
   3588         long rawContactId = createRawContact();
   3589         ContentValues values = buildGenericStreamItemValues();
   3590         Uri resultUri = insertStreamItem(rawContactId, values, null);
   3591         long streamItemId = ContentUris.parseId(resultUri);
   3592         ContentValues photoValues = buildGenericStreamItemPhotoValues(1);
   3593         resultUri = insertStreamItemPhoto(streamItemId, photoValues, null);
   3594         long streamItemPhotoId = ContentUris.parseId(resultUri);
   3595 
   3596         photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
   3597                 R.drawable.nebula, PhotoSize.ORIGINAL));
   3598         Uri photoUri =
   3599                 ContentUris.withAppendedId(
   3600                         Uri.withAppendedPath(
   3601                                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3602                                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
   3603                         streamItemPhotoId);
   3604         mResolver.update(photoUri, photoValues, null, null);
   3605         photoValues.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
   3606         assertStoredValues(photoUri, photoValues);
   3607 
   3608         // Check that the photo stored is the expected one.
   3609         String displayPhotoUri = getStoredValue(photoUri, StreamItemPhotos.PHOTO_URI);
   3610         assertInputStreamContent(loadPhotoFromResource(R.drawable.nebula, PhotoSize.DISPLAY_PHOTO),
   3611                 mResolver.openInputStream(Uri.parse(displayPhotoUri)));
   3612     }
   3613 
   3614     public void testUpdateStreamItemPhotoWithContentValues() throws IOException {
   3615         long rawContactId = createRawContact();
   3616         ContentValues values = buildGenericStreamItemValues();
   3617         Uri resultUri = insertStreamItem(rawContactId, values, null);
   3618         long streamItemId = ContentUris.parseId(resultUri);
   3619         ContentValues photoValues = buildGenericStreamItemPhotoValues(1);
   3620         resultUri = insertStreamItemPhoto(streamItemId, photoValues, null);
   3621         long streamItemPhotoId = ContentUris.parseId(resultUri);
   3622 
   3623         photoValues.put(StreamItemPhotos._ID, streamItemPhotoId);
   3624         photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
   3625                 R.drawable.nebula, PhotoSize.ORIGINAL));
   3626         Uri photoUri =
   3627                 Uri.withAppendedPath(
   3628                         ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3629                         StreamItems.StreamItemPhotos.CONTENT_DIRECTORY);
   3630         mResolver.update(photoUri, photoValues, null, null);
   3631         photoValues.remove(StreamItemPhotos.PHOTO);  // Removed during processing.
   3632         assertStoredValues(photoUri, photoValues);
   3633 
   3634         // Check that the photo stored is the expected one.
   3635         String displayPhotoUri = getStoredValue(photoUri, StreamItemPhotos.PHOTO_URI);
   3636         assertInputStreamContent(loadPhotoFromResource(R.drawable.nebula, PhotoSize.DISPLAY_PHOTO),
   3637                 mResolver.openInputStream(Uri.parse(displayPhotoUri)));
   3638     }
   3639 
   3640     public void testUpdateStreamItemPhotoFromOtherAccount() {
   3641         long rawContactId = createRawContact(mAccount);
   3642         ContentValues values = buildGenericStreamItemValues();
   3643         Uri resultUri = insertStreamItem(rawContactId, values, mAccount);
   3644         long streamItemId = ContentUris.parseId(resultUri);
   3645         ContentValues photoValues = buildGenericStreamItemPhotoValues(1);
   3646         resultUri = insertStreamItemPhoto(streamItemId, photoValues, mAccount);
   3647         long streamItemPhotoId = ContentUris.parseId(resultUri);
   3648 
   3649         photoValues.put(StreamItemPhotos._ID, streamItemPhotoId);
   3650         photoValues.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(
   3651                 R.drawable.galaxy, PhotoSize.ORIGINAL));
   3652         Uri photoUri =
   3653                 maybeAddAccountQueryParameters(
   3654                         Uri.withAppendedPath(
   3655                                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3656                                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
   3657                         mAccountTwo);
   3658         try {
   3659             mResolver.update(photoUri, photoValues, null, null);
   3660             fail("Should not be able to update stream item photos inserted by another account");
   3661         } catch (SecurityException expected) {
   3662             // Can't update a stream item photo inserted by another account.
   3663         }
   3664     }
   3665 
   3666     // Stream item deletion test cases.
   3667 
   3668     public void testDeleteStreamItemById() {
   3669         long rawContactId = createRawContact();
   3670         ContentValues firstValues = buildGenericStreamItemValues();
   3671         Uri resultUri = insertStreamItem(rawContactId, firstValues, null);
   3672         long firstStreamItemId = ContentUris.parseId(resultUri);
   3673 
   3674         ContentValues secondValues = buildGenericStreamItemValues();
   3675         secondValues.put(StreamItems.TEXT, "Goodbye world");
   3676         insertStreamItem(rawContactId, secondValues, null);
   3677 
   3678         // Delete the first stream item.
   3679         mResolver.delete(ContentUris.withAppendedId(StreamItems.CONTENT_URI, firstStreamItemId),
   3680                 null, null);
   3681 
   3682         // Check that only the second item remains.
   3683         assertStoredValues(Uri.withAppendedPath(
   3684                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3685                 RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues);
   3686     }
   3687 
   3688     public void testDeleteStreamItemWithSelection() {
   3689         long rawContactId = createRawContact();
   3690         ContentValues firstValues = buildGenericStreamItemValues();
   3691         insertStreamItem(rawContactId, firstValues, null);
   3692 
   3693         ContentValues secondValues = buildGenericStreamItemValues();
   3694         secondValues.put(StreamItems.TEXT, "Goodbye world");
   3695         insertStreamItem(rawContactId, secondValues, null);
   3696 
   3697         // Delete the first stream item with a custom selection.
   3698         mResolver.delete(StreamItems.CONTENT_URI, StreamItems.TEXT + "=?",
   3699                 new String[]{"Hello world"});
   3700 
   3701         // Check that only the second item remains.
   3702         assertStoredValues(Uri.withAppendedPath(
   3703                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3704                 RawContacts.StreamItems.CONTENT_DIRECTORY), secondValues);
   3705     }
   3706 
   3707     public void testDeleteStreamItemFromOtherAccount() {
   3708         long rawContactId = createRawContact(mAccount);
   3709         long streamItemId = ContentUris.parseId(
   3710                 insertStreamItem(rawContactId, buildGenericStreamItemValues(), mAccount));
   3711         try {
   3712             mResolver.delete(
   3713                     maybeAddAccountQueryParameters(
   3714                             ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3715                             mAccountTwo), null, null);
   3716             fail("Should not be able to delete stream item inserted by another account");
   3717         } catch (SecurityException expected) {
   3718             // Can't delete a stream item from another account.
   3719         }
   3720     }
   3721 
   3722     // Stream item photo deletion test cases.
   3723 
   3724     public void testDeleteStreamItemPhotoById() {
   3725         long rawContactId = createRawContact();
   3726         long streamItemId = ContentUris.parseId(
   3727                 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null));
   3728         long streamItemPhotoId = ContentUris.parseId(
   3729                 insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), null));
   3730         mResolver.delete(
   3731                 ContentUris.withAppendedId(
   3732                         Uri.withAppendedPath(
   3733                                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3734                                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
   3735                         streamItemPhotoId), null, null);
   3736 
   3737         Cursor c = mResolver.query(StreamItems.CONTENT_PHOTO_URI,
   3738                 new String[]{StreamItemPhotos._ID},
   3739                 StreamItemPhotos.STREAM_ITEM_ID + "=?", new String[]{String.valueOf(streamItemId)},
   3740                 null);
   3741         try {
   3742             assertEquals("Expected photo to be deleted.", 0, c.getCount());
   3743         } finally {
   3744             c.close();
   3745         }
   3746     }
   3747 
   3748     public void testDeleteStreamItemPhotoWithSelection() {
   3749         long rawContactId = createRawContact();
   3750         long streamItemId = ContentUris.parseId(
   3751                 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null));
   3752         ContentValues firstPhotoValues = buildGenericStreamItemPhotoValues(0);
   3753         ContentValues secondPhotoValues = buildGenericStreamItemPhotoValues(1);
   3754         insertStreamItemPhoto(streamItemId, firstPhotoValues, null);
   3755         firstPhotoValues.remove(StreamItemPhotos.PHOTO);  // Removed while processing.
   3756         insertStreamItemPhoto(streamItemId, secondPhotoValues, null);
   3757         Uri photoUri = Uri.withAppendedPath(
   3758                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3759                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY);
   3760         mResolver.delete(photoUri, StreamItemPhotos.SORT_INDEX + "=1", null);
   3761 
   3762         assertStoredValues(photoUri, firstPhotoValues);
   3763     }
   3764 
   3765     public void testDeleteStreamItemPhotoFromOtherAccount() {
   3766         long rawContactId = createRawContact(mAccount);
   3767         long streamItemId = ContentUris.parseId(
   3768                 insertStreamItem(rawContactId, buildGenericStreamItemValues(), mAccount));
   3769         insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), mAccount);
   3770         try {
   3771             mResolver.delete(maybeAddAccountQueryParameters(
   3772                     Uri.withAppendedPath(
   3773                             ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3774                             StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
   3775                     mAccountTwo), null, null);
   3776             fail("Should not be able to delete stream item photo inserted by another account");
   3777         } catch (SecurityException expected) {
   3778             // Can't delete a stream item photo from another account.
   3779         }
   3780     }
   3781 
   3782     public void testDeleteStreamItemsWhenRawContactDeleted() {
   3783         long rawContactId = createRawContact(mAccount);
   3784         Uri streamItemUri = insertStreamItem(rawContactId,
   3785                 buildGenericStreamItemValues(), mAccount);
   3786         Uri streamItemPhotoUri = insertStreamItemPhoto(ContentUris.parseId(streamItemUri),
   3787                         buildGenericStreamItemPhotoValues(0), mAccount);
   3788         mResolver.delete(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3789                 null, null);
   3790 
   3791         ContentValues[] emptyValues = new ContentValues[0];
   3792 
   3793         // The stream item and its photo should be gone.
   3794         assertStoredValues(streamItemUri, emptyValues);
   3795         assertStoredValues(streamItemPhotoUri, emptyValues);
   3796     }
   3797 
   3798     public void testQueryStreamItemLimit() {
   3799         ContentValues values = new ContentValues();
   3800         values.put(StreamItems.MAX_ITEMS, 5);
   3801         assertStoredValues(StreamItems.CONTENT_LIMIT_URI, values);
   3802     }
   3803 
   3804     // Tests for inserting or updating stream items as a side-effect of making status updates
   3805     // (forward-compatibility of status updates into the new social stream API).
   3806 
   3807     public void testStreamItemInsertedOnStatusUpdate() {
   3808 
   3809         // This method of creating a raw contact automatically inserts a status update with
   3810         // the status message "hacking".
   3811         ContentValues values = new ContentValues();
   3812         long rawContactId = createRawContact(values, "18004664411",
   3813                 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   3814                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
   3815                         StatusUpdates.CAPABILITY_HAS_VOICE);
   3816 
   3817         ContentValues expectedValues = new ContentValues();
   3818         expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
   3819         expectedValues.put(StreamItems.TEXT, "hacking");
   3820         assertStoredValues(RawContacts.CONTENT_URI.buildUpon()
   3821                 .appendPath(String.valueOf(rawContactId))
   3822                 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(),
   3823                 expectedValues);
   3824     }
   3825 
   3826     public void testStreamItemInsertedOnStatusUpdate_HtmlQuoting() {
   3827 
   3828         // This method of creating a raw contact automatically inserts a status update with
   3829         // the status message "hacking".
   3830         ContentValues values = new ContentValues();
   3831         long rawContactId = createRawContact(values, "18004664411",
   3832                 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   3833                 StatusUpdates.CAPABILITY_HAS_VOICE);
   3834 
   3835         // Insert a new status update for the raw contact.
   3836         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411 (at) acme.com",
   3837                 StatusUpdates.INVISIBLE, "& <b> test &#39;", StatusUpdates.CAPABILITY_HAS_VOICE);
   3838 
   3839         ContentValues expectedValues = new ContentValues();
   3840         expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
   3841         expectedValues.put(StreamItems.TEXT, "&amp; &lt;b&gt; test &amp;#39;");
   3842         assertStoredValues(RawContacts.CONTENT_URI.buildUpon()
   3843                 .appendPath(String.valueOf(rawContactId))
   3844                 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(),
   3845                 expectedValues);
   3846     }
   3847 
   3848     public void testStreamItemUpdatedOnSecondStatusUpdate() {
   3849 
   3850         // This method of creating a raw contact automatically inserts a status update with
   3851         // the status message "hacking".
   3852         ContentValues values = new ContentValues();
   3853         int chatMode = StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
   3854                 StatusUpdates.CAPABILITY_HAS_VOICE;
   3855         long rawContactId = createRawContact(values, "18004664411",
   3856                 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, chatMode);
   3857 
   3858         // Insert a new status update for the raw contact.
   3859         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "goog411 (at) acme.com",
   3860                 StatusUpdates.INVISIBLE, "finished hacking", chatMode);
   3861 
   3862         ContentValues expectedValues = new ContentValues();
   3863         expectedValues.put(StreamItems.RAW_CONTACT_ID, rawContactId);
   3864         expectedValues.put(StreamItems.TEXT, "finished hacking");
   3865         assertStoredValues(RawContacts.CONTENT_URI.buildUpon()
   3866                 .appendPath(String.valueOf(rawContactId))
   3867                 .appendPath(RawContacts.StreamItems.CONTENT_DIRECTORY).build(),
   3868                 expectedValues);
   3869     }
   3870 
   3871     public void testStreamItemReadRequiresReadSocialStreamPermission() {
   3872         long rawContactId = createRawContact();
   3873         long contactId = queryContactId(rawContactId);
   3874         String lookupKey = queryLookupKey(contactId);
   3875         long streamItemId = ContentUris.parseId(
   3876                 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null));
   3877         mActor.removePermissions("android.permission.READ_SOCIAL_STREAM");
   3878 
   3879         // Try selecting the stream item in various ways.
   3880         expectSecurityException(
   3881                 "Querying stream items by contact ID requires social stream read permission",
   3882                 Uri.withAppendedPath(
   3883                         ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   3884                         Contacts.StreamItems.CONTENT_DIRECTORY), null, null, null, null);
   3885 
   3886         expectSecurityException(
   3887                 "Querying stream items by lookup key requires social stream read permission",
   3888                 Contacts.CONTENT_LOOKUP_URI.buildUpon().appendPath(lookupKey)
   3889                         .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY).build(),
   3890                 null, null, null, null);
   3891 
   3892         expectSecurityException(
   3893                 "Querying stream items by lookup key and ID requires social stream read permission",
   3894                 Uri.withAppendedPath(Contacts.getLookupUri(contactId, lookupKey),
   3895                         Contacts.StreamItems.CONTENT_DIRECTORY),
   3896                 null, null, null, null);
   3897 
   3898         expectSecurityException(
   3899                 "Querying stream items by raw contact ID requires social stream read permission",
   3900                 Uri.withAppendedPath(
   3901                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3902                         RawContacts.StreamItems.CONTENT_DIRECTORY), null, null, null, null);
   3903 
   3904         expectSecurityException(
   3905                 "Querying stream items by raw contact ID and stream item ID requires social " +
   3906                         "stream read permission",
   3907                 ContentUris.withAppendedId(
   3908                         Uri.withAppendedPath(
   3909                                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   3910                                 RawContacts.StreamItems.CONTENT_DIRECTORY),
   3911                         streamItemId), null, null, null, null);
   3912 
   3913         expectSecurityException(
   3914                 "Querying all stream items requires social stream read permission",
   3915                 StreamItems.CONTENT_URI, null, null, null, null);
   3916 
   3917         expectSecurityException(
   3918                 "Querying stream item by ID requires social stream read permission",
   3919                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3920                 null, null, null, null);
   3921     }
   3922 
   3923     public void testStreamItemPhotoReadRequiresReadSocialStreamPermission() {
   3924         long rawContactId = createRawContact();
   3925         long streamItemId = ContentUris.parseId(
   3926                 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null));
   3927         long streamItemPhotoId = ContentUris.parseId(
   3928                 insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), null));
   3929         mActor.removePermissions("android.permission.READ_SOCIAL_STREAM");
   3930 
   3931         // Try selecting the stream item photo in various ways.
   3932         expectSecurityException(
   3933                 "Querying all stream item photos requires social stream read permission",
   3934                 StreamItems.CONTENT_URI.buildUpon()
   3935                         .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY).build(),
   3936                 null, null, null, null);
   3937 
   3938         expectSecurityException(
   3939                 "Querying all stream item photos requires social stream read permission",
   3940                 StreamItems.CONTENT_URI.buildUpon()
   3941                         .appendPath(String.valueOf(streamItemId))
   3942                         .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY)
   3943                         .appendPath(String.valueOf(streamItemPhotoId)).build(),
   3944                 null, null, null, null);
   3945     }
   3946 
   3947     public void testStreamItemModificationRequiresWriteSocialStreamPermission() {
   3948         long rawContactId = createRawContact();
   3949         long streamItemId = ContentUris.parseId(
   3950                 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null));
   3951         mActor.removePermissions("android.permission.WRITE_SOCIAL_STREAM");
   3952 
   3953         try {
   3954             insertStreamItem(rawContactId, buildGenericStreamItemValues(), null);
   3955             fail("Should not be able to insert to stream without write social stream permission");
   3956         } catch (SecurityException expected) {
   3957         }
   3958 
   3959         try {
   3960             ContentValues values = new ContentValues();
   3961             values.put(StreamItems.TEXT, "Goodbye world");
   3962             mResolver.update(ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3963                     values, null, null);
   3964             fail("Should not be able to update stream without write social stream permission");
   3965         } catch (SecurityException expected) {
   3966         }
   3967 
   3968         try {
   3969             mResolver.delete(ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   3970                     null, null);
   3971             fail("Should not be able to delete from stream without write social stream permission");
   3972         } catch (SecurityException expected) {
   3973         }
   3974     }
   3975 
   3976     public void testStreamItemPhotoModificationRequiresWriteSocialStreamPermission() {
   3977         long rawContactId = createRawContact();
   3978         long streamItemId = ContentUris.parseId(
   3979                 insertStreamItem(rawContactId, buildGenericStreamItemValues(), null));
   3980         long streamItemPhotoId = ContentUris.parseId(
   3981                 insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(0), null));
   3982         mActor.removePermissions("android.permission.WRITE_SOCIAL_STREAM");
   3983 
   3984         Uri photoUri = StreamItems.CONTENT_URI.buildUpon()
   3985                 .appendPath(String.valueOf(streamItemId))
   3986                 .appendPath(StreamItems.StreamItemPhotos.CONTENT_DIRECTORY)
   3987                 .appendPath(String.valueOf(streamItemPhotoId)).build();
   3988 
   3989         try {
   3990             insertStreamItemPhoto(streamItemId, buildGenericStreamItemPhotoValues(1), null);
   3991             fail("Should not be able to insert photos without write social stream permission");
   3992         } catch (SecurityException expected) {
   3993         }
   3994 
   3995         try {
   3996             ContentValues values = new ContentValues();
   3997             values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(R.drawable.galaxy,
   3998                     PhotoSize.ORIGINAL));
   3999             mResolver.update(photoUri, values, null, null);
   4000             fail("Should not be able to update photos without write social stream permission");
   4001         } catch (SecurityException expected) {
   4002         }
   4003 
   4004         try {
   4005             mResolver.delete(photoUri, null, null);
   4006             fail("Should not be able to delete photos without write social stream permission");
   4007         } catch (SecurityException expected) {
   4008         }
   4009     }
   4010 
   4011     public void testStatusUpdateDoesNotRequireReadOrWriteSocialStreamPermission() {
   4012         int protocol1 = Im.PROTOCOL_GOOGLE_TALK;
   4013         String handle1 = "test (at) gmail.com";
   4014         long rawContactId = createRawContact();
   4015         insertImHandle(rawContactId, protocol1, null, handle1);
   4016         mActor.removePermissions("android.permission.READ_SOCIAL_STREAM");
   4017         mActor.removePermissions("android.permission.WRITE_SOCIAL_STREAM");
   4018 
   4019         insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AVAILABLE, "Green",
   4020                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   4021 
   4022         mActor.addPermissions("android.permission.READ_SOCIAL_STREAM");
   4023 
   4024         ContentValues expectedValues = new ContentValues();
   4025         expectedValues.put(StreamItems.TEXT, "Green");
   4026         assertStoredValues(Uri.withAppendedPath(
   4027                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   4028                         RawContacts.StreamItems.CONTENT_DIRECTORY), expectedValues);
   4029     }
   4030 
   4031     private ContentValues buildGenericStreamItemValues() {
   4032         ContentValues values = new ContentValues();
   4033         values.put(StreamItems.TEXT, "Hello world");
   4034         values.put(StreamItems.TIMESTAMP, System.currentTimeMillis());
   4035         values.put(StreamItems.COMMENTS, "Reshared by 123 others");
   4036         return values;
   4037     }
   4038 
   4039     private ContentValues buildGenericStreamItemPhotoValues(int sortIndex) {
   4040         ContentValues values = new ContentValues();
   4041         values.put(StreamItemPhotos.SORT_INDEX, sortIndex);
   4042         values.put(StreamItemPhotos.PHOTO,
   4043                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.ORIGINAL));
   4044         return values;
   4045     }
   4046 
   4047     public void testSingleStatusUpdateRowPerContact() {
   4048         int protocol1 = Im.PROTOCOL_GOOGLE_TALK;
   4049         String handle1 = "test (at) gmail.com";
   4050 
   4051         long rawContactId1 = createRawContact();
   4052         insertImHandle(rawContactId1, protocol1, null, handle1);
   4053 
   4054         insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AVAILABLE, "Green",
   4055                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   4056         insertStatusUpdate(protocol1, null, handle1, StatusUpdates.AWAY, "Yellow",
   4057                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   4058         insertStatusUpdate(protocol1, null, handle1, StatusUpdates.INVISIBLE, "Red",
   4059                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   4060 
   4061         Cursor c = queryContact(queryContactId(rawContactId1),
   4062                 new String[] {Contacts.CONTACT_PRESENCE, Contacts.CONTACT_STATUS});
   4063         assertEquals(1, c.getCount());
   4064 
   4065         c.moveToFirst();
   4066         assertEquals(StatusUpdates.INVISIBLE, c.getInt(0));
   4067         assertEquals("Red", c.getString(1));
   4068         c.close();
   4069     }
   4070 
   4071     private void updateSendToVoicemailAndRingtone(long contactId, boolean sendToVoicemail,
   4072             String ringtone) {
   4073         ContentValues values = new ContentValues();
   4074         values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail);
   4075         if (ringtone != null) {
   4076             values.put(Contacts.CUSTOM_RINGTONE, ringtone);
   4077         }
   4078 
   4079         final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   4080         int count = mResolver.update(uri, values, null, null);
   4081         assertEquals(1, count);
   4082     }
   4083 
   4084     private void updateSendToVoicemailAndRingtoneWithSelection(long contactId,
   4085             boolean sendToVoicemail, String ringtone) {
   4086         ContentValues values = new ContentValues();
   4087         values.put(Contacts.SEND_TO_VOICEMAIL, sendToVoicemail);
   4088         if (ringtone != null) {
   4089             values.put(Contacts.CUSTOM_RINGTONE, ringtone);
   4090         }
   4091 
   4092         int count = mResolver.update(Contacts.CONTENT_URI, values, Contacts._ID + "=" + contactId,
   4093                 null);
   4094         assertEquals(1, count);
   4095     }
   4096 
   4097     private void assertSendToVoicemailAndRingtone(long contactId, boolean expectedSendToVoicemail,
   4098             String expectedRingtone) {
   4099         Cursor c = queryContact(contactId);
   4100         assertTrue(c.moveToNext());
   4101         int sendToVoicemail = c.getInt(c.getColumnIndex(Contacts.SEND_TO_VOICEMAIL));
   4102         assertEquals(expectedSendToVoicemail ? 1 : 0, sendToVoicemail);
   4103         String ringtone = c.getString(c.getColumnIndex(Contacts.CUSTOM_RINGTONE));
   4104         if (expectedRingtone == null) {
   4105             assertNull(ringtone);
   4106         } else {
   4107             assertTrue(ArrayUtils.contains(expectedRingtone.split(","), ringtone));
   4108         }
   4109         c.close();
   4110     }
   4111 
   4112     public void testContactVisibilityUpdateOnMembershipChange() {
   4113         long rawContactId = createRawContact(mAccount);
   4114         assertVisibility(rawContactId, "0");
   4115 
   4116         long visibleGroupId = createGroup(mAccount, "123", "Visible", 1);
   4117         long invisibleGroupId = createGroup(mAccount, "567", "Invisible", 0);
   4118 
   4119         Uri membership1 = insertGroupMembership(rawContactId, visibleGroupId);
   4120         assertVisibility(rawContactId, "1");
   4121 
   4122         Uri membership2 = insertGroupMembership(rawContactId, invisibleGroupId);
   4123         assertVisibility(rawContactId, "1");
   4124 
   4125         mResolver.delete(membership1, null, null);
   4126         assertVisibility(rawContactId, "0");
   4127 
   4128         ContentValues values = new ContentValues();
   4129         values.put(GroupMembership.GROUP_ROW_ID, visibleGroupId);
   4130 
   4131         mResolver.update(membership2, values, null, null);
   4132         assertVisibility(rawContactId, "1");
   4133     }
   4134 
   4135     private void assertVisibility(long rawContactId, String expectedValue) {
   4136         assertStoredValue(Contacts.CONTENT_URI, Contacts._ID + "=" + queryContactId(rawContactId),
   4137                 null, Contacts.IN_VISIBLE_GROUP, expectedValue);
   4138     }
   4139 
   4140     public void testSupplyingBothValuesAndParameters() throws Exception {
   4141         Account account = new Account("account 1", "type%/:1");
   4142         Uri uri = ContactsContract.Groups.CONTENT_URI.buildUpon()
   4143                 .appendQueryParameter(ContactsContract.Groups.ACCOUNT_NAME, account.name)
   4144                 .appendQueryParameter(ContactsContract.Groups.ACCOUNT_TYPE, account.type)
   4145                 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
   4146                 .build();
   4147 
   4148         ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(uri);
   4149         builder.withValue(ContactsContract.Groups.ACCOUNT_TYPE, account.type);
   4150         builder.withValue(ContactsContract.Groups.ACCOUNT_NAME, account.name);
   4151         builder.withValue(ContactsContract.Groups.SYSTEM_ID, "some id");
   4152         builder.withValue(ContactsContract.Groups.TITLE, "some name");
   4153         builder.withValue(ContactsContract.Groups.GROUP_VISIBLE, 1);
   4154 
   4155         mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(builder.build()));
   4156 
   4157         builder = ContentProviderOperation.newInsert(uri);
   4158         builder.withValue(ContactsContract.Groups.ACCOUNT_TYPE, account.type + "diff");
   4159         builder.withValue(ContactsContract.Groups.ACCOUNT_NAME, account.name);
   4160         builder.withValue(ContactsContract.Groups.SYSTEM_ID, "some other id");
   4161         builder.withValue(ContactsContract.Groups.TITLE, "some other name");
   4162         builder.withValue(ContactsContract.Groups.GROUP_VISIBLE, 1);
   4163 
   4164         try {
   4165             mResolver.applyBatch(ContactsContract.AUTHORITY, Lists.newArrayList(builder.build()));
   4166             fail("Expected IllegalArgumentException");
   4167         } catch (IllegalArgumentException ex) {
   4168             // Expected
   4169         }
   4170     }
   4171 
   4172     public void testContentEntityIterator() {
   4173         // create multiple contacts and check that the selected ones are returned
   4174         long id;
   4175 
   4176         long groupId1 = createGroup(mAccount, "gsid1", "title1");
   4177         long groupId2 = createGroup(mAccount, "gsid2", "title2");
   4178 
   4179         id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c0");
   4180         insertGroupMembership(id, "gsid1");
   4181         insertEmail(id, "c0 (at) email.com");
   4182         insertPhoneNumber(id, "5551212c0");
   4183 
   4184         long c1 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c1");
   4185         Uri id_1_0 = insertGroupMembership(id, "gsid1");
   4186         Uri id_1_1 = insertGroupMembership(id, "gsid2");
   4187         Uri id_1_2 = insertEmail(id, "c1 (at) email.com");
   4188         Uri id_1_3 = insertPhoneNumber(id, "5551212c1");
   4189 
   4190         long c2 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c2");
   4191         Uri id_2_0 = insertGroupMembership(id, "gsid1");
   4192         Uri id_2_1 = insertEmail(id, "c2 (at) email.com");
   4193         Uri id_2_2 = insertPhoneNumber(id, "5551212c2");
   4194 
   4195         long c3 = id = createRawContact(mAccount, RawContacts.SOURCE_ID, "c3");
   4196         Uri id_3_0 = insertGroupMembership(id, groupId2);
   4197         Uri id_3_1 = insertEmail(id, "c3 (at) email.com");
   4198         Uri id_3_2 = insertPhoneNumber(id, "5551212c3");
   4199 
   4200         EntityIterator iterator = RawContacts.newEntityIterator(mResolver.query(
   4201                 maybeAddAccountQueryParameters(RawContactsEntity.CONTENT_URI, mAccount), null,
   4202                 RawContacts.SOURCE_ID + " in ('c1', 'c2', 'c3')", null, null));
   4203         Entity entity;
   4204         ContentValues[] subValues;
   4205         entity = iterator.next();
   4206         assertEquals(c1, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
   4207         subValues = asSortedContentValuesArray(entity.getSubValues());
   4208         assertEquals(4, subValues.length);
   4209         assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
   4210                 Data._ID, id_1_0,
   4211                 GroupMembership.GROUP_ROW_ID, groupId1,
   4212                 GroupMembership.GROUP_SOURCE_ID, "gsid1");
   4213         assertDataRow(subValues[1], GroupMembership.CONTENT_ITEM_TYPE,
   4214                 Data._ID, id_1_1,
   4215                 GroupMembership.GROUP_ROW_ID, groupId2,
   4216                 GroupMembership.GROUP_SOURCE_ID, "gsid2");
   4217         assertDataRow(subValues[2], Email.CONTENT_ITEM_TYPE,
   4218                 Data._ID, id_1_2,
   4219                 Email.DATA, "c1 (at) email.com");
   4220         assertDataRow(subValues[3], Phone.CONTENT_ITEM_TYPE,
   4221                 Data._ID, id_1_3,
   4222                 Email.DATA, "5551212c1");
   4223 
   4224         entity = iterator.next();
   4225         assertEquals(c2, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
   4226         subValues = asSortedContentValuesArray(entity.getSubValues());
   4227         assertEquals(3, subValues.length);
   4228         assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
   4229                 Data._ID, id_2_0,
   4230                 GroupMembership.GROUP_ROW_ID, groupId1,
   4231                 GroupMembership.GROUP_SOURCE_ID, "gsid1");
   4232         assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
   4233                 Data._ID, id_2_1,
   4234                 Email.DATA, "c2 (at) email.com");
   4235         assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
   4236                 Data._ID, id_2_2,
   4237                 Email.DATA, "5551212c2");
   4238 
   4239         entity = iterator.next();
   4240         assertEquals(c3, (long) entity.getEntityValues().getAsLong(RawContacts._ID));
   4241         subValues = asSortedContentValuesArray(entity.getSubValues());
   4242         assertEquals(3, subValues.length);
   4243         assertDataRow(subValues[0], GroupMembership.CONTENT_ITEM_TYPE,
   4244                 Data._ID, id_3_0,
   4245                 GroupMembership.GROUP_ROW_ID, groupId2,
   4246                 GroupMembership.GROUP_SOURCE_ID, "gsid2");
   4247         assertDataRow(subValues[1], Email.CONTENT_ITEM_TYPE,
   4248                 Data._ID, id_3_1,
   4249                 Email.DATA, "c3 (at) email.com");
   4250         assertDataRow(subValues[2], Phone.CONTENT_ITEM_TYPE,
   4251                 Data._ID, id_3_2,
   4252                 Email.DATA, "5551212c3");
   4253 
   4254         assertFalse(iterator.hasNext());
   4255         iterator.close();
   4256     }
   4257 
   4258     public void testDataCreateUpdateDeleteByMimeType() throws Exception {
   4259         long rawContactId = createRawContact();
   4260 
   4261         ContentValues values = new ContentValues();
   4262         values.put(Data.RAW_CONTACT_ID, rawContactId);
   4263         values.put(Data.MIMETYPE, "testmimetype");
   4264         values.put(Data.RES_PACKAGE, "oldpackage");
   4265         values.put(Data.IS_PRIMARY, 1);
   4266         values.put(Data.IS_SUPER_PRIMARY, 1);
   4267         values.put(Data.DATA1, "old1");
   4268         values.put(Data.DATA2, "old2");
   4269         values.put(Data.DATA3, "old3");
   4270         values.put(Data.DATA4, "old4");
   4271         values.put(Data.DATA5, "old5");
   4272         values.put(Data.DATA6, "old6");
   4273         values.put(Data.DATA7, "old7");
   4274         values.put(Data.DATA8, "old8");
   4275         values.put(Data.DATA9, "old9");
   4276         values.put(Data.DATA10, "old10");
   4277         values.put(Data.DATA11, "old11");
   4278         values.put(Data.DATA12, "old12");
   4279         values.put(Data.DATA13, "old13");
   4280         values.put(Data.DATA14, "old14");
   4281         values.put(Data.DATA15, "old15");
   4282         Uri uri = mResolver.insert(Data.CONTENT_URI, values);
   4283         assertStoredValues(uri, values);
   4284         assertNetworkNotified(true);
   4285 
   4286         values.clear();
   4287         values.put(Data.RES_PACKAGE, "newpackage");
   4288         values.put(Data.IS_PRIMARY, 0);
   4289         values.put(Data.IS_SUPER_PRIMARY, 0);
   4290         values.put(Data.DATA1, "new1");
   4291         values.put(Data.DATA2, "new2");
   4292         values.put(Data.DATA3, "new3");
   4293         values.put(Data.DATA4, "new4");
   4294         values.put(Data.DATA5, "new5");
   4295         values.put(Data.DATA6, "new6");
   4296         values.put(Data.DATA7, "new7");
   4297         values.put(Data.DATA8, "new8");
   4298         values.put(Data.DATA9, "new9");
   4299         values.put(Data.DATA10, "new10");
   4300         values.put(Data.DATA11, "new11");
   4301         values.put(Data.DATA12, "new12");
   4302         values.put(Data.DATA13, "new13");
   4303         values.put(Data.DATA14, "new14");
   4304         values.put(Data.DATA15, "new15");
   4305         mResolver.update(Data.CONTENT_URI, values, Data.RAW_CONTACT_ID + "=" + rawContactId +
   4306                 " AND " + Data.MIMETYPE + "='testmimetype'", null);
   4307         assertNetworkNotified(true);
   4308 
   4309         assertStoredValues(uri, values);
   4310 
   4311         int count = mResolver.delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
   4312                 + " AND " + Data.MIMETYPE + "='testmimetype'", null);
   4313         assertEquals(1, count);
   4314         assertEquals(0, getCount(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=" + rawContactId
   4315                         + " AND " + Data.MIMETYPE + "='testmimetype'", null));
   4316         assertNetworkNotified(true);
   4317     }
   4318 
   4319     public void testRawContactQuery() {
   4320         Account account1 = new Account("a", "b");
   4321         Account account2 = new Account("c", "d");
   4322         long rawContactId1 = createRawContact(account1);
   4323         long rawContactId2 = createRawContact(account2);
   4324 
   4325         Uri uri1 = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account1);
   4326         Uri uri2 = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, account2);
   4327         assertEquals(1, getCount(uri1, null, null));
   4328         assertEquals(1, getCount(uri2, null, null));
   4329         assertStoredValue(uri1, RawContacts._ID, rawContactId1) ;
   4330         assertStoredValue(uri2, RawContacts._ID, rawContactId2) ;
   4331 
   4332         Uri rowUri1 = ContentUris.withAppendedId(uri1, rawContactId1);
   4333         Uri rowUri2 = ContentUris.withAppendedId(uri2, rawContactId2);
   4334         assertStoredValue(rowUri1, RawContacts._ID, rawContactId1) ;
   4335         assertStoredValue(rowUri2, RawContacts._ID, rawContactId2) ;
   4336     }
   4337 
   4338     public void testRawContactDeletion() {
   4339         long rawContactId = createRawContact(mAccount);
   4340         Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
   4341 
   4342         insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com");
   4343         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com",
   4344                 StatusUpdates.AVAILABLE, null,
   4345                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   4346         long contactId = queryContactId(rawContactId);
   4347 
   4348         assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
   4349                 null, null));
   4350         assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
   4351                 + rawContactId, null));
   4352 
   4353         mResolver.delete(uri, null, null);
   4354 
   4355         assertStoredValue(uri, RawContacts.DELETED, "1");
   4356         assertNetworkNotified(true);
   4357 
   4358         Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount);
   4359         mResolver.delete(permanentDeletionUri, null, null);
   4360         assertEquals(0, getCount(uri, null, null));
   4361         assertEquals(0, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
   4362                 null, null));
   4363         assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
   4364                 + rawContactId, null));
   4365         assertEquals(0, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null));
   4366         assertNetworkNotified(false);
   4367     }
   4368 
   4369     public void testRawContactDeletionKeepingAggregateContact() {
   4370         long rawContactId1 = createRawContactWithName(mAccount);
   4371         long rawContactId2 = createRawContactWithName(mAccount);
   4372         setAggregationException(
   4373                 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
   4374 
   4375         long contactId = queryContactId(rawContactId1);
   4376 
   4377         Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
   4378         Uri permanentDeletionUri = setCallerIsSyncAdapter(uri, mAccount);
   4379         mResolver.delete(permanentDeletionUri, null, null);
   4380         assertEquals(0, getCount(uri, null, null));
   4381         assertEquals(1, getCount(Contacts.CONTENT_URI, Contacts._ID + "=" + contactId, null));
   4382     }
   4383 
   4384     public void testRawContactDeletionWithAccounts() {
   4385         long rawContactId = createRawContact(mAccount);
   4386         Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
   4387 
   4388         insertImHandle(rawContactId, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com");
   4389         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com",
   4390                 StatusUpdates.AVAILABLE, null,
   4391                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   4392         assertEquals(1, getCount(Uri.withAppendedPath(uri, RawContacts.Data.CONTENT_DIRECTORY),
   4393                 null, null));
   4394         assertEquals(1, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
   4395                 + rawContactId, null));
   4396 
   4397         // Do not delete if we are deleting with wrong account.
   4398         Uri deleteWithWrongAccountUri =
   4399             RawContacts.CONTENT_URI.buildUpon()
   4400                 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccountTwo.name)
   4401                 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccountTwo.type)
   4402                 .build();
   4403         mResolver.delete(deleteWithWrongAccountUri, null, null);
   4404 
   4405         assertStoredValue(uri, RawContacts.DELETED, "0");
   4406 
   4407         // Delete if we are deleting with correct account.
   4408         Uri deleteWithCorrectAccountUri =
   4409             RawContacts.CONTENT_URI.buildUpon()
   4410                 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccount.name)
   4411                 .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccount.type)
   4412                 .build();
   4413         mResolver.delete(deleteWithCorrectAccountUri, null, null);
   4414 
   4415         assertStoredValue(uri, RawContacts.DELETED, "1");
   4416     }
   4417 
   4418     public void testAccountsUpdated() {
   4419         // This is to ensure we do not delete contacts with null, null (account name, type)
   4420         // accidentally.
   4421         long rawContactId3 = createRawContactWithName("James", "Sullivan");
   4422         insertPhoneNumber(rawContactId3, "5234567890");
   4423         Uri rawContact3 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId3);
   4424         assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null));
   4425 
   4426         ContactsProvider2 cp = (ContactsProvider2) getProvider();
   4427         mActor.setAccounts(new Account[]{mAccount, mAccountTwo});
   4428         cp.onAccountsUpdated(new Account[]{mAccount, mAccountTwo});
   4429         assertEquals(1, getCount(RawContacts.CONTENT_URI, null, null));
   4430         assertStoredValue(rawContact3, RawContacts.ACCOUNT_NAME, null);
   4431         assertStoredValue(rawContact3, RawContacts.ACCOUNT_TYPE, null);
   4432 
   4433         long rawContactId1 = createRawContact(mAccount);
   4434         insertEmail(rawContactId1, "account1 (at) email.com");
   4435         long rawContactId2 = createRawContact(mAccountTwo);
   4436         insertEmail(rawContactId2, "account2 (at) email.com");
   4437         insertImHandle(rawContactId2, Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com");
   4438         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "deleteme (at) android.com",
   4439                 StatusUpdates.AVAILABLE, null,
   4440                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   4441 
   4442         mActor.setAccounts(new Account[]{mAccount});
   4443         cp.onAccountsUpdated(new Account[]{mAccount});
   4444         assertEquals(2, getCount(RawContacts.CONTENT_URI, null, null));
   4445         assertEquals(0, getCount(StatusUpdates.CONTENT_URI, PresenceColumns.RAW_CONTACT_ID + "="
   4446                 + rawContactId2, null));
   4447     }
   4448 
   4449     public void testAccountDeletion() {
   4450         Account readOnlyAccount = new Account("act", READ_ONLY_ACCOUNT_TYPE);
   4451         ContactsProvider2 cp = (ContactsProvider2) getProvider();
   4452         mActor.setAccounts(new Account[]{readOnlyAccount, mAccount});
   4453         cp.onAccountsUpdated(new Account[]{readOnlyAccount, mAccount});
   4454 
   4455         long rawContactId1 = createRawContactWithName("John", "Doe", readOnlyAccount);
   4456         Uri photoUri1 = insertPhoto(rawContactId1);
   4457         long rawContactId2 = createRawContactWithName("john", "doe", mAccount);
   4458         Uri photoUri2 = insertPhoto(rawContactId2);
   4459         storeValue(photoUri2, Photo.IS_SUPER_PRIMARY, "1");
   4460 
   4461         assertAggregated(rawContactId1, rawContactId2);
   4462 
   4463         long contactId = queryContactId(rawContactId1);
   4464 
   4465         // The display name should come from the writable account
   4466         assertStoredValue(Uri.withAppendedPath(
   4467                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   4468                 Contacts.Data.CONTENT_DIRECTORY),
   4469                 Contacts.DISPLAY_NAME, "john doe");
   4470 
   4471         // The photo should be the one we marked as super-primary
   4472         assertStoredValue(Contacts.CONTENT_URI, contactId,
   4473                 Contacts.PHOTO_ID, ContentUris.parseId(photoUri2));
   4474 
   4475         mActor.setAccounts(new Account[]{readOnlyAccount});
   4476         // Remove the writable account
   4477         cp.onAccountsUpdated(new Account[]{readOnlyAccount});
   4478 
   4479         // The display name should come from the remaining account
   4480         assertStoredValue(Uri.withAppendedPath(
   4481                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   4482                 Contacts.Data.CONTENT_DIRECTORY),
   4483                 Contacts.DISPLAY_NAME, "John Doe");
   4484 
   4485         // The photo should be the remaining one
   4486         assertStoredValue(Contacts.CONTENT_URI, contactId,
   4487                 Contacts.PHOTO_ID, ContentUris.parseId(photoUri1));
   4488     }
   4489 
   4490     public void testStreamItemsCleanedUpOnAccountRemoval() {
   4491         Account doomedAccount = new Account("doom", "doom");
   4492         Account safeAccount = mAccount;
   4493         ContactsProvider2 cp = (ContactsProvider2) getProvider();
   4494         mActor.setAccounts(new Account[]{doomedAccount, safeAccount});
   4495         cp.onAccountsUpdated(new Account[]{doomedAccount, safeAccount});
   4496 
   4497         // Create a doomed raw contact, stream item, and photo.
   4498         long doomedRawContactId = createRawContactWithName(doomedAccount);
   4499         Uri doomedStreamItemUri =
   4500                 insertStreamItem(doomedRawContactId, buildGenericStreamItemValues(), doomedAccount);
   4501         long doomedStreamItemId = ContentUris.parseId(doomedStreamItemUri);
   4502         Uri doomedStreamItemPhotoUri = insertStreamItemPhoto(
   4503                 doomedStreamItemId, buildGenericStreamItemPhotoValues(0), doomedAccount);
   4504 
   4505         // Create a safe raw contact, stream item, and photo.
   4506         long safeRawContactId = createRawContactWithName(safeAccount);
   4507         Uri safeStreamItemUri =
   4508                 insertStreamItem(safeRawContactId, buildGenericStreamItemValues(), safeAccount);
   4509         long safeStreamItemId = ContentUris.parseId(safeStreamItemUri);
   4510         Uri safeStreamItemPhotoUri = insertStreamItemPhoto(
   4511                 safeStreamItemId, buildGenericStreamItemPhotoValues(0), safeAccount);
   4512         long safeStreamItemPhotoId = ContentUris.parseId(safeStreamItemPhotoUri);
   4513 
   4514         // Remove the doomed account.
   4515         mActor.setAccounts(new Account[]{safeAccount});
   4516         cp.onAccountsUpdated(new Account[]{safeAccount});
   4517 
   4518         // Check that the doomed stuff has all been nuked.
   4519         ContentValues[] noValues = new ContentValues[0];
   4520         assertStoredValues(ContentUris.withAppendedId(RawContacts.CONTENT_URI, doomedRawContactId),
   4521                 noValues);
   4522         assertStoredValues(doomedStreamItemUri, noValues);
   4523         assertStoredValues(doomedStreamItemPhotoUri, noValues);
   4524 
   4525         // Check that the safe stuff lives on.
   4526         assertStoredValue(RawContacts.CONTENT_URI, safeRawContactId, RawContacts._ID,
   4527                 safeRawContactId);
   4528         assertStoredValue(safeStreamItemUri, StreamItems._ID, safeStreamItemId);
   4529         assertStoredValue(safeStreamItemPhotoUri, StreamItemPhotos._ID, safeStreamItemPhotoId);
   4530     }
   4531 
   4532     public void testContactDeletion() {
   4533         long rawContactId1 = createRawContactWithName("John", "Doe", ACCOUNT_1);
   4534         long rawContactId2 = createRawContactWithName("John", "Doe", ACCOUNT_2);
   4535 
   4536         long contactId = queryContactId(rawContactId1);
   4537 
   4538         mResolver.delete(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), null, null);
   4539 
   4540         assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1),
   4541                 RawContacts.DELETED, "1");
   4542         assertStoredValue(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2),
   4543                 RawContacts.DELETED, "1");
   4544     }
   4545 
   4546     public void testMarkAsDirtyParameter() {
   4547         long rawContactId = createRawContact(mAccount);
   4548         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
   4549 
   4550         Uri uri = insertStructuredName(rawContactId, "John", "Doe");
   4551         clearDirty(rawContactUri);
   4552         Uri updateUri = setCallerIsSyncAdapter(uri, mAccount);
   4553 
   4554         ContentValues values = new ContentValues();
   4555         values.put(StructuredName.FAMILY_NAME, "Dough");
   4556         mResolver.update(updateUri, values, null, null);
   4557         assertStoredValue(uri, StructuredName.FAMILY_NAME, "Dough");
   4558         assertDirty(rawContactUri, false);
   4559         assertNetworkNotified(false);
   4560     }
   4561 
   4562     public void testRawContactDirtyAndVersion() {
   4563         final long rawContactId = createRawContact(mAccount);
   4564         Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId);
   4565         assertDirty(uri, false);
   4566         long version = getVersion(uri);
   4567 
   4568         ContentValues values = new ContentValues();
   4569         values.put(ContactsContract.RawContacts.DIRTY, 0);
   4570         values.put(ContactsContract.RawContacts.SEND_TO_VOICEMAIL, 1);
   4571         values.put(ContactsContract.RawContacts.AGGREGATION_MODE,
   4572                 RawContacts.AGGREGATION_MODE_IMMEDIATE);
   4573         values.put(ContactsContract.RawContacts.STARRED, 1);
   4574         assertEquals(1, mResolver.update(uri, values, null, null));
   4575         assertEquals(version, getVersion(uri));
   4576 
   4577         assertDirty(uri, false);
   4578         assertNetworkNotified(false);
   4579 
   4580         Uri emailUri = insertEmail(rawContactId, "goo (at) woo.com");
   4581         assertDirty(uri, true);
   4582         assertNetworkNotified(true);
   4583         ++version;
   4584         assertEquals(version, getVersion(uri));
   4585         clearDirty(uri);
   4586 
   4587         values = new ContentValues();
   4588         values.put(Email.DATA, "goo (at) hoo.com");
   4589         mResolver.update(emailUri, values, null, null);
   4590         assertDirty(uri, true);
   4591         assertNetworkNotified(true);
   4592         ++version;
   4593         assertEquals(version, getVersion(uri));
   4594         clearDirty(uri);
   4595 
   4596         mResolver.delete(emailUri, null, null);
   4597         assertDirty(uri, true);
   4598         assertNetworkNotified(true);
   4599         ++version;
   4600         assertEquals(version, getVersion(uri));
   4601     }
   4602 
   4603     public void testRawContactClearDirty() {
   4604         final long rawContactId = createRawContact(mAccount);
   4605         Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
   4606                 rawContactId);
   4607         long version = getVersion(uri);
   4608         insertEmail(rawContactId, "goo (at) woo.com");
   4609         assertDirty(uri, true);
   4610         version++;
   4611         assertEquals(version, getVersion(uri));
   4612 
   4613         clearDirty(uri);
   4614         assertDirty(uri, false);
   4615         assertEquals(version, getVersion(uri));
   4616     }
   4617 
   4618     public void testRawContactDeletionSetsDirty() {
   4619         final long rawContactId = createRawContact(mAccount);
   4620         Uri uri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
   4621                 rawContactId);
   4622         long version = getVersion(uri);
   4623         clearDirty(uri);
   4624         assertDirty(uri, false);
   4625 
   4626         mResolver.delete(uri, null, null);
   4627         assertStoredValue(uri, RawContacts.DELETED, "1");
   4628         assertDirty(uri, true);
   4629         assertNetworkNotified(true);
   4630         version++;
   4631         assertEquals(version, getVersion(uri));
   4632     }
   4633 
   4634     public void testDeleteContactWithoutName() {
   4635         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
   4636         long rawContactId = ContentUris.parseId(rawContactUri);
   4637 
   4638         Uri phoneUri = insertPhoneNumber(rawContactId, "555-123-45678", true);
   4639 
   4640         long contactId = queryContactId(rawContactId);
   4641         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   4642         Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
   4643 
   4644         int numDeleted = mResolver.delete(lookupUri, null, null);
   4645         assertEquals(1, numDeleted);
   4646     }
   4647 
   4648     public void testDeleteContactWithoutAnyData() {
   4649         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
   4650         long rawContactId = ContentUris.parseId(rawContactUri);
   4651 
   4652         long contactId = queryContactId(rawContactId);
   4653         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   4654         Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
   4655 
   4656         int numDeleted = mResolver.delete(lookupUri, null, null);
   4657         assertEquals(1, numDeleted);
   4658     }
   4659 
   4660     public void testDeleteContactWithEscapedUri() {
   4661         ContentValues values = new ContentValues();
   4662         values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
   4663         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   4664         long rawContactId = ContentUris.parseId(rawContactUri);
   4665 
   4666         long contactId = queryContactId(rawContactId);
   4667         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   4668         Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
   4669         assertEquals(1, mResolver.delete(lookupUri, null, null));
   4670     }
   4671 
   4672     public void testQueryContactWithEscapedUri() {
   4673         ContentValues values = new ContentValues();
   4674         values.put(RawContacts.SOURCE_ID, "!@#$%^&*()_+=-/.,<>?;'\":[]}{\\|`~");
   4675         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   4676         long rawContactId = ContentUris.parseId(rawContactUri);
   4677 
   4678         long contactId = queryContactId(rawContactId);
   4679         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   4680         Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
   4681         Cursor c = mResolver.query(lookupUri, null, null, null, "");
   4682         assertEquals(1, c.getCount());
   4683         c.close();
   4684     }
   4685 
   4686     public void testGetPhotoUri() {
   4687         ContentValues values = new ContentValues();
   4688         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   4689         long rawContactId = ContentUris.parseId(rawContactUri);
   4690         insertStructuredName(rawContactId, "John", "Doe");
   4691         long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
   4692         long photoFileId = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?",
   4693                 new String[]{String.valueOf(dataId)}, Photo.PHOTO_FILE_ID);
   4694         String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId)
   4695                 .toString();
   4696 
   4697         assertStoredValue(
   4698                 ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId)),
   4699                 Contacts.PHOTO_URI, photoUri);
   4700     }
   4701 
   4702     public void testGetPhotoViaLookupUri() throws IOException {
   4703         long rawContactId = createRawContact();
   4704         long contactId = queryContactId(rawContactId);
   4705         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   4706         Uri lookupUri = Contacts.getLookupUri(mResolver, contactUri);
   4707         String lookupKey = lookupUri.getPathSegments().get(2);
   4708         insertPhoto(rawContactId, R.drawable.earth_small);
   4709         byte[] thumbnail = loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL);
   4710 
   4711         // Two forms of lookup key URIs should be valid - one with the contact ID, one without.
   4712         Uri photoLookupUriWithId = Uri.withAppendedPath(lookupUri, "photo");
   4713         Uri photoLookupUriWithoutId = Contacts.CONTENT_LOOKUP_URI.buildUpon()
   4714                 .appendPath(lookupKey).appendPath("photo").build();
   4715 
   4716         // Try retrieving as a data record.
   4717         ContentValues values = new ContentValues();
   4718         values.put(Photo.PHOTO, thumbnail);
   4719         assertStoredValues(photoLookupUriWithId, values);
   4720         assertStoredValues(photoLookupUriWithoutId, values);
   4721 
   4722         // Try opening as an input stream.
   4723         assertInputStreamContent(thumbnail, mResolver.openInputStream(photoLookupUriWithId));
   4724         assertInputStreamContent(thumbnail, mResolver.openInputStream(photoLookupUriWithoutId));
   4725     }
   4726 
   4727     public void testInputStreamForPhoto() throws Exception {
   4728         long rawContactId = createRawContact();
   4729         long contactId = queryContactId(rawContactId);
   4730         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   4731         insertPhoto(rawContactId);
   4732         Uri photoUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_URI));
   4733         Uri photoThumbnailUri = Uri.parse(getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI));
   4734 
   4735         assertInputStreamContent(loadTestPhoto(PhotoSize.DISPLAY_PHOTO),
   4736                 mResolver.openInputStream(photoUri));
   4737         assertInputStreamContent(loadTestPhoto(PhotoSize.THUMBNAIL),
   4738                 mResolver.openInputStream(photoThumbnailUri));
   4739     }
   4740 
   4741     private static void assertInputStreamContent(byte[] expected, InputStream is)
   4742             throws IOException {
   4743         try {
   4744             byte[] observed = new byte[expected.length];
   4745             int count = is.read(observed);
   4746             assertEquals(expected.length, count);
   4747             assertEquals(-1, is.read());
   4748             MoreAsserts.assertEquals(expected, observed);
   4749         } finally {
   4750             is.close();
   4751         }
   4752     }
   4753 
   4754     public void testSuperPrimaryPhoto() {
   4755         long rawContactId1 = createRawContact(new Account("a", "a"));
   4756         Uri photoUri1 = insertPhoto(rawContactId1, R.drawable.earth_normal);
   4757         long photoId1 = ContentUris.parseId(photoUri1);
   4758 
   4759         long rawContactId2 = createRawContact(new Account("b", "b"));
   4760         Uri photoUri2 = insertPhoto(rawContactId2, R.drawable.earth_normal);
   4761         long photoId2 = ContentUris.parseId(photoUri2);
   4762 
   4763         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
   4764                 rawContactId1, rawContactId2);
   4765 
   4766         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
   4767                 queryContactId(rawContactId1));
   4768 
   4769         long photoFileId1 = getStoredLongValue(Data.CONTENT_URI, Data._ID + "=?",
   4770                 new String[]{String.valueOf(photoId1)}, Photo.PHOTO_FILE_ID);
   4771         String photoUri = ContentUris.withAppendedId(DisplayPhoto.CONTENT_URI, photoFileId1)
   4772                 .toString();
   4773         assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
   4774         assertStoredValue(contactUri, Contacts.PHOTO_URI, photoUri);
   4775 
   4776         setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
   4777                 rawContactId1, rawContactId2);
   4778 
   4779         ContentValues values = new ContentValues();
   4780         values.put(Data.IS_SUPER_PRIMARY, 1);
   4781         mResolver.update(photoUri2, values, null, null);
   4782 
   4783         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
   4784                 rawContactId1, rawContactId2);
   4785         contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
   4786                 queryContactId(rawContactId1));
   4787         assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId2);
   4788 
   4789         mResolver.update(photoUri1, values, null, null);
   4790         assertStoredValue(contactUri, Contacts.PHOTO_ID, photoId1);
   4791     }
   4792 
   4793     public void testUpdatePhoto() {
   4794         ContentValues values = new ContentValues();
   4795         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   4796         long rawContactId = ContentUris.parseId(rawContactUri);
   4797         insertStructuredName(rawContactId, "John", "Doe");
   4798 
   4799         Uri twigUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
   4800                 queryContactId(rawContactId)), Contacts.Photo.CONTENT_DIRECTORY);
   4801 
   4802         values.clear();
   4803         values.put(Data.RAW_CONTACT_ID, rawContactId);
   4804         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
   4805         values.putNull(Photo.PHOTO);
   4806         Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
   4807         long photoId = ContentUris.parseId(dataUri);
   4808 
   4809         assertEquals(0, getCount(twigUri, null, null));
   4810 
   4811         values.clear();
   4812         values.put(Photo.PHOTO, loadTestPhoto());
   4813         mResolver.update(dataUri, values, null, null);
   4814         assertNetworkNotified(true);
   4815 
   4816         long twigId = getStoredLongValue(twigUri, Data._ID);
   4817         assertEquals(photoId, twigId);
   4818     }
   4819 
   4820     public void testUpdateRawContactDataPhoto() {
   4821         // setup a contact with a null photo
   4822         ContentValues values = new ContentValues();
   4823         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   4824         long rawContactId = ContentUris.parseId(rawContactUri);
   4825 
   4826         // setup a photo
   4827         values.put(Data.RAW_CONTACT_ID, rawContactId);
   4828         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
   4829         values.putNull(Photo.PHOTO);
   4830 
   4831         // try to do an update before insert should return count == 0
   4832         Uri dataUri = Uri.withAppendedPath(
   4833                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
   4834                 RawContacts.Data.CONTENT_DIRECTORY);
   4835         assertEquals(0, mResolver.update(dataUri, values, Data.MIMETYPE + "=?",
   4836                 new String[] {Photo.CONTENT_ITEM_TYPE}));
   4837 
   4838         mResolver.insert(Data.CONTENT_URI, values);
   4839 
   4840         // save a photo to the db
   4841         values.clear();
   4842         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
   4843         values.put(Photo.PHOTO, loadTestPhoto());
   4844         assertEquals(1, mResolver.update(dataUri, values, Data.MIMETYPE + "=?",
   4845                 new String[] {Photo.CONTENT_ITEM_TYPE}));
   4846 
   4847         // verify the photo
   4848         Cursor storedPhoto = mResolver.query(dataUri, new String[] {Photo.PHOTO},
   4849                 Data.MIMETYPE + "=?", new String[] {Photo.CONTENT_ITEM_TYPE}, null);
   4850         storedPhoto.moveToFirst();
   4851         MoreAsserts.assertEquals(loadTestPhoto(PhotoSize.THUMBNAIL), storedPhoto.getBlob(0));
   4852         storedPhoto.close();
   4853     }
   4854 
   4855     public void testOpenDisplayPhotoForContactId() throws IOException {
   4856         long rawContactId = createRawContactWithName();
   4857         long contactId = queryContactId(rawContactId);
   4858         insertPhoto(rawContactId, R.drawable.earth_normal);
   4859         Uri photoUri = Contacts.CONTENT_URI.buildUpon()
   4860                 .appendPath(String.valueOf(contactId))
   4861                 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
   4862         assertInputStreamContent(
   4863                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
   4864                 mResolver.openInputStream(photoUri));
   4865     }
   4866 
   4867     public void testOpenDisplayPhotoForContactLookupKey() throws IOException {
   4868         long rawContactId = createRawContactWithName();
   4869         long contactId = queryContactId(rawContactId);
   4870         String lookupKey = queryLookupKey(contactId);
   4871         insertPhoto(rawContactId, R.drawable.earth_normal);
   4872         Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon()
   4873                 .appendPath(lookupKey)
   4874                 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
   4875         assertInputStreamContent(
   4876                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
   4877                 mResolver.openInputStream(photoUri));
   4878     }
   4879 
   4880     public void testOpenDisplayPhotoForContactLookupKeyAndId() throws IOException {
   4881         long rawContactId = createRawContactWithName();
   4882         long contactId = queryContactId(rawContactId);
   4883         String lookupKey = queryLookupKey(contactId);
   4884         insertPhoto(rawContactId, R.drawable.earth_normal);
   4885         Uri photoUri = Contacts.CONTENT_LOOKUP_URI.buildUpon()
   4886                 .appendPath(lookupKey)
   4887                 .appendPath(String.valueOf(contactId))
   4888                 .appendPath(Contacts.Photo.DISPLAY_PHOTO).build();
   4889         assertInputStreamContent(
   4890                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
   4891                 mResolver.openInputStream(photoUri));
   4892     }
   4893 
   4894     public void testOpenDisplayPhotoForRawContactId() throws IOException {
   4895         long rawContactId = createRawContactWithName();
   4896         insertPhoto(rawContactId, R.drawable.earth_normal);
   4897         Uri photoUri = RawContacts.CONTENT_URI.buildUpon()
   4898                 .appendPath(String.valueOf(rawContactId))
   4899                 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
   4900         assertInputStreamContent(
   4901                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
   4902                 mResolver.openInputStream(photoUri));
   4903     }
   4904 
   4905     public void testOpenDisplayPhotoByPhotoUri() throws IOException {
   4906         long rawContactId = createRawContactWithName();
   4907         long contactId = queryContactId(rawContactId);
   4908         insertPhoto(rawContactId, R.drawable.earth_normal);
   4909 
   4910         // Get the photo URI out and check the content.
   4911         String photoUri = getStoredValue(
   4912                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   4913                 Contacts.PHOTO_URI);
   4914         assertInputStreamContent(
   4915                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
   4916                 mResolver.openInputStream(Uri.parse(photoUri)));
   4917     }
   4918 
   4919     public void testPhotoUriForDisplayPhoto() {
   4920         long rawContactId = createRawContactWithName();
   4921         long contactId = queryContactId(rawContactId);
   4922 
   4923         // Photo being inserted is larger than a thumbnail, so it will be stored as a file.
   4924         long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
   4925         String photoFileId = getStoredValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
   4926                 Photo.PHOTO_FILE_ID);
   4927         String photoUri = getStoredValue(
   4928                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   4929                 Contacts.PHOTO_URI);
   4930 
   4931         // Check that the photo URI differs from the thumbnail.
   4932         String thumbnailUri = getStoredValue(
   4933                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   4934                 Contacts.PHOTO_THUMBNAIL_URI);
   4935         assertFalse(photoUri.equals(thumbnailUri));
   4936 
   4937         // URI should be of the form display_photo/ID
   4938         assertEquals(Uri.withAppendedPath(DisplayPhoto.CONTENT_URI, photoFileId).toString(),
   4939                 photoUri);
   4940     }
   4941 
   4942     public void testPhotoUriForThumbnailPhoto() throws IOException {
   4943         long rawContactId = createRawContactWithName();
   4944         long contactId = queryContactId(rawContactId);
   4945 
   4946         // Photo being inserted is a thumbnail, so it will only be stored in a BLOB.  The photo URI
   4947         // will fall back to the thumbnail URI.
   4948         insertPhoto(rawContactId, R.drawable.earth_small);
   4949         String photoUri = getStoredValue(
   4950                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   4951                 Contacts.PHOTO_URI);
   4952 
   4953         // Check that the photo URI is equal to the thumbnail URI.
   4954         String thumbnailUri = getStoredValue(
   4955                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   4956                 Contacts.PHOTO_THUMBNAIL_URI);
   4957         assertEquals(photoUri, thumbnailUri);
   4958 
   4959         // URI should be of the form contacts/ID/photo
   4960         assertEquals(Uri.withAppendedPath(
   4961                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   4962                 Contacts.Photo.CONTENT_DIRECTORY).toString(),
   4963                 photoUri);
   4964 
   4965         // Loading the photo URI content should get the thumbnail.
   4966         assertInputStreamContent(
   4967                 loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL),
   4968                 mResolver.openInputStream(Uri.parse(photoUri)));
   4969     }
   4970 
   4971     public void testWriteNewPhotoToAssetFile() throws Exception {
   4972         long rawContactId = createRawContactWithName();
   4973         long contactId = queryContactId(rawContactId);
   4974 
   4975         // Load in a huge photo.
   4976         final byte[] originalPhoto = loadPhotoFromResource(
   4977                 R.drawable.earth_huge, PhotoSize.ORIGINAL);
   4978 
   4979         // Write it out.
   4980         final Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon()
   4981                 .appendPath(String.valueOf(rawContactId))
   4982                 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
   4983         writePhotoAsync(writeablePhotoUri, originalPhoto);
   4984 
   4985         // Check that the display photo and thumbnail have been set.
   4986         String photoUri = null;
   4987         for (int i = 0; i < 10 && photoUri == null; i++) {
   4988             // Wait a tick for the photo processing to occur.
   4989             Thread.sleep(100);
   4990             photoUri = getStoredValue(
   4991                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   4992                 Contacts.PHOTO_URI);
   4993         }
   4994 
   4995         assertFalse(TextUtils.isEmpty(photoUri));
   4996         String thumbnailUri = getStoredValue(
   4997                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   4998                 Contacts.PHOTO_THUMBNAIL_URI);
   4999         assertFalse(TextUtils.isEmpty(thumbnailUri));
   5000         assertNotSame(photoUri, thumbnailUri);
   5001 
   5002         // Check the content of the display photo and thumbnail.
   5003         assertInputStreamContent(
   5004                 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO),
   5005                 mResolver.openInputStream(Uri.parse(photoUri)));
   5006         assertInputStreamContent(
   5007                 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL),
   5008                 mResolver.openInputStream(Uri.parse(thumbnailUri)));
   5009     }
   5010 
   5011     public void testWriteUpdatedPhotoToAssetFile() throws Exception {
   5012         long rawContactId = createRawContactWithName();
   5013         long contactId = queryContactId(rawContactId);
   5014 
   5015         // Insert a large photo first.
   5016         insertPhoto(rawContactId, R.drawable.earth_large);
   5017         String largeEarthPhotoUri = getStoredValue(
   5018                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI);
   5019 
   5020         // Load in a huge photo.
   5021         byte[] originalPhoto = loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL);
   5022 
   5023         // Write it out.
   5024         Uri writeablePhotoUri = RawContacts.CONTENT_URI.buildUpon()
   5025                 .appendPath(String.valueOf(rawContactId))
   5026                 .appendPath(RawContacts.DisplayPhoto.CONTENT_DIRECTORY).build();
   5027         writePhotoAsync(writeablePhotoUri, originalPhoto);
   5028 
   5029         // Allow a second for processing to occur.
   5030         Thread.sleep(1000);
   5031 
   5032         // Check that the display photo URI has been modified.
   5033         String hugeEarthPhotoUri = getStoredValue(
   5034                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), Contacts.PHOTO_URI);
   5035         assertFalse(hugeEarthPhotoUri.equals(largeEarthPhotoUri));
   5036 
   5037         // Check the content of the display photo and thumbnail.
   5038         String hugeEarthThumbnailUri = getStoredValue(
   5039                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
   5040                 Contacts.PHOTO_THUMBNAIL_URI);
   5041         assertInputStreamContent(
   5042                 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.DISPLAY_PHOTO),
   5043                 mResolver.openInputStream(Uri.parse(hugeEarthPhotoUri)));
   5044         assertInputStreamContent(
   5045                 loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.THUMBNAIL),
   5046                 mResolver.openInputStream(Uri.parse(hugeEarthThumbnailUri)));
   5047 
   5048     }
   5049 
   5050     private void writePhotoAsync(final Uri uri, final byte[] photoBytes) throws Exception {
   5051         AsyncTask<Object, Object, Object> task = new AsyncTask<Object, Object, Object>() {
   5052             @Override
   5053             protected Object doInBackground(Object... params) {
   5054                 OutputStream os;
   5055                 try {
   5056                     os = mResolver.openOutputStream(uri, "rw");
   5057                     os.write(photoBytes);
   5058                     os.close();
   5059                     return null;
   5060                 } catch (IOException ioe) {
   5061                     throw new RuntimeException(ioe);
   5062                 }
   5063             }
   5064         };
   5065         task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[])null).get();
   5066     }
   5067 
   5068     public void testPhotoDimensionLimits() {
   5069         ContentValues values = new ContentValues();
   5070         values.put(DisplayPhoto.DISPLAY_MAX_DIM, 256);
   5071         values.put(DisplayPhoto.THUMBNAIL_MAX_DIM, 96);
   5072         assertStoredValues(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, values);
   5073     }
   5074 
   5075     public void testPhotoStoreCleanup() throws IOException {
   5076         SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider;
   5077         PhotoStore photoStore = provider.getPhotoStore();
   5078 
   5079         // Trigger an initial cleanup so another one won't happen while we're running this test.
   5080         provider.cleanupPhotoStore();
   5081 
   5082         // Insert a couple of contacts with photos.
   5083         long rawContactId1 = createRawContactWithName();
   5084         long contactId1 = queryContactId(rawContactId1);
   5085         long dataId1 = ContentUris.parseId(insertPhoto(rawContactId1, R.drawable.earth_normal));
   5086         long photoFileId1 =
   5087                 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId1),
   5088                         Photo.PHOTO_FILE_ID);
   5089 
   5090         long rawContactId2 = createRawContactWithName();
   5091         long contactId2 = queryContactId(rawContactId2);
   5092         long dataId2 = ContentUris.parseId(insertPhoto(rawContactId2, R.drawable.earth_normal));
   5093         long photoFileId2 =
   5094                 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2),
   5095                         Photo.PHOTO_FILE_ID);
   5096 
   5097         // Update the second raw contact with a different photo.
   5098         ContentValues values = new ContentValues();
   5099         values.put(Data.RAW_CONTACT_ID, rawContactId2);
   5100         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
   5101         values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_huge, PhotoSize.ORIGINAL));
   5102         assertEquals(1, mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?",
   5103                 new String[]{String.valueOf(dataId2)}));
   5104         long replacementPhotoFileId =
   5105                 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId2),
   5106                         Photo.PHOTO_FILE_ID);
   5107 
   5108         // Insert a third raw contact that has a bogus photo file ID.
   5109         long bogusFileId = 1234567;
   5110         long rawContactId3 = createRawContactWithName();
   5111         long contactId3 = queryContactId(rawContactId3);
   5112         values.clear();
   5113         values.put(Data.RAW_CONTACT_ID, rawContactId3);
   5114         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
   5115         values.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_normal,
   5116                 PhotoSize.THUMBNAIL));
   5117         values.put(Photo.PHOTO_FILE_ID, bogusFileId);
   5118         values.put(DataRowHandlerForPhoto.SKIP_PROCESSING_KEY, true);
   5119         mResolver.insert(Data.CONTENT_URI, values);
   5120 
   5121         // Insert a fourth raw contact with a stream item that has a photo, then remove that photo
   5122         // from the photo store.
   5123         Account socialAccount = new Account("social", "social");
   5124         long rawContactId4 = createRawContactWithName(socialAccount);
   5125         Uri streamItemUri =
   5126                 insertStreamItem(rawContactId4, buildGenericStreamItemValues(), socialAccount);
   5127         long streamItemId = ContentUris.parseId(streamItemUri);
   5128         Uri streamItemPhotoUri = insertStreamItemPhoto(
   5129                 streamItemId, buildGenericStreamItemPhotoValues(0), socialAccount);
   5130         long streamItemPhotoFileId = getStoredLongValue(streamItemPhotoUri,
   5131                 StreamItemPhotos.PHOTO_FILE_ID);
   5132         photoStore.remove(streamItemPhotoFileId);
   5133 
   5134         // Also insert a bogus photo that nobody is using.
   5135         long bogusPhotoId = photoStore.insert(new PhotoProcessor(loadPhotoFromResource(
   5136                 R.drawable.earth_huge, PhotoSize.ORIGINAL), 256, 96));
   5137 
   5138         // Manually trigger another cleanup in the provider.
   5139         provider.cleanupPhotoStore();
   5140 
   5141         // The following things should have happened.
   5142 
   5143         // 1. Raw contact 1 and its photo remain unaffected.
   5144         assertEquals(photoFileId1, (long) getStoredLongValue(
   5145                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1),
   5146                 Contacts.PHOTO_FILE_ID));
   5147 
   5148         // 2. Raw contact 2 retains its new photo.  The old one is deleted from the photo store.
   5149         assertEquals(replacementPhotoFileId, (long) getStoredLongValue(
   5150                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2),
   5151                 Contacts.PHOTO_FILE_ID));
   5152         assertNull(photoStore.get(photoFileId2));
   5153 
   5154         // 3. Raw contact 3 should have its photo file reference cleared.
   5155         assertNull(getStoredValue(
   5156                 ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId3),
   5157                 Contacts.PHOTO_FILE_ID));
   5158 
   5159         // 4. The bogus photo that nobody was using should be cleared from the photo store.
   5160         assertNull(photoStore.get(bogusPhotoId));
   5161 
   5162         // 5. The bogus stream item photo should be cleared from the stream item.
   5163         assertStoredValues(Uri.withAppendedPath(
   5164                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   5165                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
   5166                 new ContentValues[0]);
   5167     }
   5168 
   5169     public void testPhotoStoreCleanupForProfile() {
   5170         SynchronousContactsProvider2 provider = (SynchronousContactsProvider2) mActor.provider;
   5171         PhotoStore profilePhotoStore = provider.getProfilePhotoStore();
   5172 
   5173         // Trigger an initial cleanup so another one won't happen while we're running this test.
   5174         provider.switchToProfileMode();
   5175         provider.cleanupPhotoStore();
   5176 
   5177         // Create the profile contact and add a photo.
   5178         Account socialAccount = new Account("social", "social");
   5179         ContentValues values = new ContentValues();
   5180         values.put(RawContacts.ACCOUNT_NAME, socialAccount.name);
   5181         values.put(RawContacts.ACCOUNT_TYPE, socialAccount.type);
   5182         long profileRawContactId = createBasicProfileContact(values);
   5183         long profileContactId = queryContactId(profileRawContactId);
   5184         long dataId = ContentUris.parseId(
   5185                 insertPhoto(profileRawContactId, R.drawable.earth_normal));
   5186         long profilePhotoFileId =
   5187                 getStoredLongValue(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
   5188                         Photo.PHOTO_FILE_ID);
   5189 
   5190         // Also add a stream item with a photo.
   5191         Uri streamItemUri =
   5192                 insertStreamItem(profileRawContactId, buildGenericStreamItemValues(),
   5193                         socialAccount);
   5194         long streamItemId = ContentUris.parseId(streamItemUri);
   5195         Uri streamItemPhotoUri = insertStreamItemPhoto(
   5196                 streamItemId, buildGenericStreamItemPhotoValues(0), socialAccount);
   5197         long streamItemPhotoFileId = getStoredLongValue(streamItemPhotoUri,
   5198                 StreamItemPhotos.PHOTO_FILE_ID);
   5199 
   5200         // Remove the stream item photo and the profile photo.
   5201         profilePhotoStore.remove(profilePhotoFileId);
   5202         profilePhotoStore.remove(streamItemPhotoFileId);
   5203 
   5204         // Manually trigger another cleanup in the provider.
   5205         provider.switchToProfileMode();
   5206         provider.cleanupPhotoStore();
   5207 
   5208         // The following things should have happened.
   5209 
   5210         // The stream item photo should have been removed.
   5211         assertStoredValues(Uri.withAppendedPath(
   5212                 ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
   5213                 StreamItems.StreamItemPhotos.CONTENT_DIRECTORY),
   5214                 new ContentValues[0]);
   5215 
   5216         // The profile photo should have been cleared.
   5217         assertNull(getStoredValue(
   5218                 ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId),
   5219                 Contacts.PHOTO_FILE_ID));
   5220 
   5221     }
   5222 
   5223     public void testOverwritePhotoWithThumbnail() throws IOException {
   5224         long rawContactId = createRawContactWithName();
   5225         long contactId = queryContactId(rawContactId);
   5226         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   5227 
   5228         // Write a regular-size photo.
   5229         long dataId = ContentUris.parseId(insertPhoto(rawContactId, R.drawable.earth_normal));
   5230         Long photoFileId = getStoredLongValue(contactUri, Contacts.PHOTO_FILE_ID);
   5231         assertTrue(photoFileId != null && photoFileId > 0);
   5232 
   5233         // Now overwrite the photo with a thumbnail-sized photo.
   5234         ContentValues update = new ContentValues();
   5235         update.put(Photo.PHOTO, loadPhotoFromResource(R.drawable.earth_small, PhotoSize.ORIGINAL));
   5236         mResolver.update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), update, null, null);
   5237 
   5238         // Photo file ID should have been nulled out, and the photo URI should be the same as the
   5239         // thumbnail URI.
   5240         assertNull(getStoredValue(contactUri, Contacts.PHOTO_FILE_ID));
   5241         String photoUri = getStoredValue(contactUri, Contacts.PHOTO_URI);
   5242         String thumbnailUri = getStoredValue(contactUri, Contacts.PHOTO_THUMBNAIL_URI);
   5243         assertEquals(photoUri, thumbnailUri);
   5244 
   5245         // Retrieving the photo URI should get the thumbnail content.
   5246         assertInputStreamContent(loadPhotoFromResource(R.drawable.earth_small, PhotoSize.THUMBNAIL),
   5247                 mResolver.openInputStream(Uri.parse(photoUri)));
   5248     }
   5249 
   5250     public void testUpdateRawContactSetStarred() {
   5251         long rawContactId1 = createRawContactWithName();
   5252         Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
   5253         long rawContactId2 = createRawContactWithName();
   5254         Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
   5255         setAggregationException(
   5256                 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
   5257 
   5258         long contactId = queryContactId(rawContactId1);
   5259         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   5260         assertStoredValue(contactUri, Contacts.STARRED, "0");
   5261 
   5262         ContentValues values = new ContentValues();
   5263         values.put(RawContacts.STARRED, "1");
   5264 
   5265         mResolver.update(rawContactUri1, values, null, null);
   5266 
   5267         assertStoredValue(rawContactUri1, RawContacts.STARRED, "1");
   5268         assertStoredValue(rawContactUri2, RawContacts.STARRED, "0");
   5269         assertStoredValue(contactUri, Contacts.STARRED, "1");
   5270 
   5271         values.put(RawContacts.STARRED, "0");
   5272         mResolver.update(rawContactUri1, values, null, null);
   5273 
   5274         assertStoredValue(rawContactUri1, RawContacts.STARRED, "0");
   5275         assertStoredValue(rawContactUri2, RawContacts.STARRED, "0");
   5276         assertStoredValue(contactUri, Contacts.STARRED, "0");
   5277 
   5278         values.put(Contacts.STARRED, "1");
   5279         mResolver.update(contactUri, values, null, null);
   5280 
   5281         assertStoredValue(rawContactUri1, RawContacts.STARRED, "1");
   5282         assertStoredValue(rawContactUri2, RawContacts.STARRED, "1");
   5283         assertStoredValue(contactUri, Contacts.STARRED, "1");
   5284     }
   5285 
   5286     public void testSetAndClearSuperPrimaryEmail() {
   5287         long rawContactId1 = createRawContact(new Account("a", "a"));
   5288         Uri mailUri11 = insertEmail(rawContactId1, "test1 (at) domain1.com");
   5289         Uri mailUri12 = insertEmail(rawContactId1, "test2 (at) domain1.com");
   5290 
   5291         long rawContactId2 = createRawContact(new Account("b", "b"));
   5292         Uri mailUri21 = insertEmail(rawContactId2, "test1 (at) domain2.com");
   5293         Uri mailUri22 = insertEmail(rawContactId2, "test2 (at) domain2.com");
   5294 
   5295         assertStoredValue(mailUri11, Data.IS_PRIMARY, 0);
   5296         assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0);
   5297         assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
   5298         assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
   5299         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
   5300         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
   5301         assertStoredValue(mailUri22, Data.IS_PRIMARY, 0);
   5302         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 0);
   5303 
   5304         // Set super primary on the first pair, primary on the second
   5305         {
   5306             ContentValues values = new ContentValues();
   5307             values.put(Data.IS_SUPER_PRIMARY, 1);
   5308             mResolver.update(mailUri11, values, null, null);
   5309         }
   5310         {
   5311             ContentValues values = new ContentValues();
   5312             values.put(Data.IS_SUPER_PRIMARY, 1);
   5313             mResolver.update(mailUri22, values, null, null);
   5314         }
   5315 
   5316         assertStoredValue(mailUri11, Data.IS_PRIMARY, 1);
   5317         assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 1);
   5318         assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
   5319         assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
   5320         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
   5321         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
   5322         assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
   5323         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
   5324 
   5325         // Clear primary on the first pair, make sure second is not affected and super_primary is
   5326         // also cleared
   5327         {
   5328             ContentValues values = new ContentValues();
   5329             values.put(Data.IS_PRIMARY, 0);
   5330             mResolver.update(mailUri11, values, null, null);
   5331         }
   5332 
   5333         assertStoredValue(mailUri11, Data.IS_PRIMARY, 0);
   5334         assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0);
   5335         assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
   5336         assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
   5337         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
   5338         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
   5339         assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
   5340         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
   5341 
   5342         // Ensure that we can only clear super_primary, if we specify the correct data row
   5343         {
   5344             ContentValues values = new ContentValues();
   5345             values.put(Data.IS_SUPER_PRIMARY, 0);
   5346             mResolver.update(mailUri21, values, null, null);
   5347         }
   5348 
   5349         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
   5350         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
   5351         assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
   5352         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
   5353 
   5354         // Ensure that we can only clear primary, if we specify the correct data row
   5355         {
   5356             ContentValues values = new ContentValues();
   5357             values.put(Data.IS_PRIMARY, 0);
   5358             mResolver.update(mailUri21, values, null, null);
   5359         }
   5360 
   5361         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
   5362         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
   5363         assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
   5364         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 1);
   5365 
   5366         // Now clear super-primary for real
   5367         {
   5368             ContentValues values = new ContentValues();
   5369             values.put(Data.IS_SUPER_PRIMARY, 0);
   5370             mResolver.update(mailUri22, values, null, null);
   5371         }
   5372 
   5373         assertStoredValue(mailUri11, Data.IS_PRIMARY, 0);
   5374         assertStoredValue(mailUri11, Data.IS_SUPER_PRIMARY, 0);
   5375         assertStoredValue(mailUri12, Data.IS_PRIMARY, 0);
   5376         assertStoredValue(mailUri12, Data.IS_SUPER_PRIMARY, 0);
   5377         assertStoredValue(mailUri21, Data.IS_PRIMARY, 0);
   5378         assertStoredValue(mailUri21, Data.IS_SUPER_PRIMARY, 0);
   5379         assertStoredValue(mailUri22, Data.IS_PRIMARY, 1);
   5380         assertStoredValue(mailUri22, Data.IS_SUPER_PRIMARY, 0);
   5381     }
   5382 
   5383     /**
   5384      * Common function for the testNewPrimaryIn* functions. Its four configurations
   5385      * are each called from its own test
   5386      */
   5387     public void testChangingPrimary(boolean inUpdate, boolean withSuperPrimary) {
   5388         long rawContactId = createRawContact(new Account("a", "a"));
   5389         Uri mailUri1 = insertEmail(rawContactId, "test1 (at) domain1.com", true);
   5390 
   5391         if (withSuperPrimary) {
   5392             final ContentValues values = new ContentValues();
   5393             values.put(Data.IS_SUPER_PRIMARY, 1);
   5394             mResolver.update(mailUri1, values, null, null);
   5395         }
   5396 
   5397         assertStoredValue(mailUri1, Data.IS_PRIMARY, 1);
   5398         assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0);
   5399 
   5400         // Insert another item
   5401         final Uri mailUri2;
   5402         if (inUpdate) {
   5403             mailUri2 = insertEmail(rawContactId, "test2 (at) domain1.com");
   5404 
   5405             assertStoredValue(mailUri1, Data.IS_PRIMARY, 1);
   5406             assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0);
   5407             assertStoredValue(mailUri2, Data.IS_PRIMARY, 0);
   5408             assertStoredValue(mailUri2, Data.IS_SUPER_PRIMARY, 0);
   5409 
   5410             final ContentValues values = new ContentValues();
   5411             values.put(Data.IS_PRIMARY, 1);
   5412             mResolver.update(mailUri2, values, null, null);
   5413         } else {
   5414             // directly add as default
   5415             mailUri2 = insertEmail(rawContactId, "test2 (at) domain1.com", true);
   5416         }
   5417 
   5418         // Ensure that primary has been unset on the first
   5419         // If withSuperPrimary is set, also ensure that is has been moved to the new item
   5420         assertStoredValue(mailUri1, Data.IS_PRIMARY, 0);
   5421         assertStoredValue(mailUri1, Data.IS_SUPER_PRIMARY, 0);
   5422         assertStoredValue(mailUri2, Data.IS_PRIMARY, 1);
   5423         assertStoredValue(mailUri2, Data.IS_SUPER_PRIMARY, withSuperPrimary ? 1 : 0);
   5424     }
   5425 
   5426     public void testNewPrimaryInInsert() {
   5427         testChangingPrimary(false, false);
   5428     }
   5429 
   5430     public void testNewPrimaryInInsertWithSuperPrimary() {
   5431         testChangingPrimary(false, true);
   5432     }
   5433 
   5434     public void testNewPrimaryInUpdate() {
   5435         testChangingPrimary(true, false);
   5436     }
   5437 
   5438     public void testNewPrimaryInUpdateWithSuperPrimary() {
   5439         testChangingPrimary(true, true);
   5440     }
   5441 
   5442     public void testContactCounts() {
   5443         Uri uri = Contacts.CONTENT_URI.buildUpon()
   5444                 .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
   5445 
   5446         createRawContact();
   5447         createRawContactWithName("James", "Sullivan");
   5448         createRawContactWithName("The Abominable", "Snowman");
   5449         createRawContactWithName("Mike", "Wazowski");
   5450         createRawContactWithName("randall", "boggs");
   5451         createRawContactWithName("Boo", null);
   5452         createRawContactWithName("Mary", null);
   5453         createRawContactWithName("Roz", null);
   5454 
   5455         Cursor cursor = mResolver.query(uri,
   5456                 new String[]{Contacts.DISPLAY_NAME},
   5457                 null, null, Contacts.SORT_KEY_PRIMARY + " COLLATE LOCALIZED");
   5458 
   5459         assertFirstLetterValues(cursor, null, "B", "J", "M", "R", "T");
   5460         assertFirstLetterCounts(cursor,    1,   1,   1,   2,   2,   1);
   5461         cursor.close();
   5462 
   5463         cursor = mResolver.query(uri,
   5464                 new String[]{Contacts.DISPLAY_NAME},
   5465                 null, null, Contacts.SORT_KEY_ALTERNATIVE + " COLLATE LOCALIZED DESC");
   5466 
   5467         assertFirstLetterValues(cursor, "W", "S", "R", "M", "B", null);
   5468         assertFirstLetterCounts(cursor,   1,   2,   1,   1,   2,    1);
   5469         cursor.close();
   5470     }
   5471 
   5472     private void assertFirstLetterValues(Cursor cursor, String... expected) {
   5473         String[] actual = cursor.getExtras()
   5474                 .getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
   5475         MoreAsserts.assertEquals(expected, actual);
   5476     }
   5477 
   5478     private void assertFirstLetterCounts(Cursor cursor, int... expected) {
   5479         int[] actual = cursor.getExtras()
   5480                 .getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
   5481         MoreAsserts.assertEquals(expected, actual);
   5482     }
   5483 
   5484     public void testReadBooleanQueryParameter() {
   5485         assertBooleanUriParameter("foo:bar", "bool", true, true);
   5486         assertBooleanUriParameter("foo:bar", "bool", false, false);
   5487         assertBooleanUriParameter("foo:bar?bool=0", "bool", true, false);
   5488         assertBooleanUriParameter("foo:bar?bool=1", "bool", false, true);
   5489         assertBooleanUriParameter("foo:bar?bool=false", "bool", true, false);
   5490         assertBooleanUriParameter("foo:bar?bool=true", "bool", false, true);
   5491         assertBooleanUriParameter("foo:bar?bool=FaLsE", "bool", true, false);
   5492         assertBooleanUriParameter("foo:bar?bool=false&some=some", "bool", true, false);
   5493         assertBooleanUriParameter("foo:bar?bool=1&some=some", "bool", false, true);
   5494         assertBooleanUriParameter("foo:bar?some=bool", "bool", true, true);
   5495         assertBooleanUriParameter("foo:bar?bool", "bool", true, true);
   5496     }
   5497 
   5498     private void assertBooleanUriParameter(String uriString, String parameter,
   5499             boolean defaultValue, boolean expectedValue) {
   5500         assertEquals(expectedValue, ContactsProvider2.readBooleanQueryParameter(
   5501                 Uri.parse(uriString), parameter, defaultValue));
   5502     }
   5503 
   5504     public void testGetQueryParameter() {
   5505         assertQueryParameter("foo:bar", "param", null);
   5506         assertQueryParameter("foo:bar?param", "param", null);
   5507         assertQueryParameter("foo:bar?param=", "param", "");
   5508         assertQueryParameter("foo:bar?param=val", "param", "val");
   5509         assertQueryParameter("foo:bar?param=val&some=some", "param", "val");
   5510         assertQueryParameter("foo:bar?some=some&param=val", "param", "val");
   5511         assertQueryParameter("foo:bar?some=some&param=val&else=else", "param", "val");
   5512         assertQueryParameter("foo:bar?param=john%40doe.com", "param", "john (at) doe.com");
   5513         assertQueryParameter("foo:bar?some_param=val", "param", null);
   5514         assertQueryParameter("foo:bar?some_param=val1&param=val2", "param", "val2");
   5515         assertQueryParameter("foo:bar?some_param=val1&param=", "param", "");
   5516         assertQueryParameter("foo:bar?some_param=val1&param", "param", null);
   5517         assertQueryParameter("foo:bar?some_param=val1&another_param=val2&param=val3",
   5518                 "param", "val3");
   5519         assertQueryParameter("foo:bar?some_param=val1&param=val2&some_param=val3",
   5520                 "param", "val2");
   5521         assertQueryParameter("foo:bar?param=val1&some_param=val2", "param", "val1");
   5522         assertQueryParameter("foo:bar?p=val1&pp=val2", "p", "val1");
   5523         assertQueryParameter("foo:bar?pp=val1&p=val2", "p", "val2");
   5524         assertQueryParameter("foo:bar?ppp=val1&pp=val2&p=val3", "p", "val3");
   5525         assertQueryParameter("foo:bar?ppp=val&", "p", null);
   5526     }
   5527 
   5528     public void testMissingAccountTypeParameter() {
   5529         // Try querying for RawContacts only using ACCOUNT_NAME
   5530         final Uri queryUri = RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(
   5531                 RawContacts.ACCOUNT_NAME, "lolwut").build();
   5532         try {
   5533             final Cursor cursor = mResolver.query(queryUri, null, null, null, null);
   5534             fail("Able to query with incomplete account query parameters");
   5535         } catch (IllegalArgumentException e) {
   5536             // Expected behavior.
   5537         }
   5538     }
   5539 
   5540     public void testInsertInconsistentAccountType() {
   5541         // Try inserting RawContact with inconsistent Accounts
   5542         final Account red = new Account("red", "red");
   5543         final Account blue = new Account("blue", "blue");
   5544 
   5545         final ContentValues values = new ContentValues();
   5546         values.put(RawContacts.ACCOUNT_NAME, red.name);
   5547         values.put(RawContacts.ACCOUNT_TYPE, red.type);
   5548 
   5549         final Uri insertUri = maybeAddAccountQueryParameters(RawContacts.CONTENT_URI, blue);
   5550         try {
   5551             mResolver.insert(insertUri, values);
   5552             fail("Able to insert RawContact with inconsistent account details");
   5553         } catch (IllegalArgumentException e) {
   5554             // Expected behavior.
   5555         }
   5556     }
   5557 
   5558     public void testProviderStatusNoContactsNoAccounts() throws Exception {
   5559         assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
   5560     }
   5561 
   5562     public void testProviderStatusOnlyLocalContacts() throws Exception {
   5563         long rawContactId = createRawContact();
   5564         assertProviderStatus(ProviderStatus.STATUS_NORMAL);
   5565         mResolver.delete(
   5566                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), null, null);
   5567         assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
   5568     }
   5569 
   5570     public void testProviderStatusWithAccounts() throws Exception {
   5571         assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
   5572         mActor.setAccounts(new Account[]{ACCOUNT_1});
   5573         ((ContactsProvider2)getProvider()).onAccountsUpdated(new Account[]{ACCOUNT_1});
   5574         assertProviderStatus(ProviderStatus.STATUS_NORMAL);
   5575         mActor.setAccounts(new Account[0]);
   5576         ((ContactsProvider2)getProvider()).onAccountsUpdated(new Account[0]);
   5577         assertProviderStatus(ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS);
   5578     }
   5579 
   5580     private void assertProviderStatus(int expectedProviderStatus) {
   5581         Cursor cursor = mResolver.query(ProviderStatus.CONTENT_URI,
   5582                 new String[]{ProviderStatus.DATA1, ProviderStatus.STATUS}, null, null, null);
   5583         assertTrue(cursor.moveToFirst());
   5584         assertEquals(0, cursor.getLong(0));
   5585         assertEquals(expectedProviderStatus, cursor.getInt(1));
   5586         cursor.close();
   5587     }
   5588 
   5589     public void testProperties() throws Exception {
   5590         ContactsProvider2 provider = (ContactsProvider2)getProvider();
   5591         ContactsDatabaseHelper helper = (ContactsDatabaseHelper)provider.getDatabaseHelper();
   5592         assertNull(helper.getProperty("non-existent", null));
   5593         assertEquals("default", helper.getProperty("non-existent", "default"));
   5594 
   5595         helper.setProperty("existent1", "string1");
   5596         helper.setProperty("existent2", "string2");
   5597         assertEquals("string1", helper.getProperty("existent1", "default"));
   5598         assertEquals("string2", helper.getProperty("existent2", "default"));
   5599         helper.setProperty("existent1", null);
   5600         assertEquals("default", helper.getProperty("existent1", "default"));
   5601     }
   5602 
   5603     private class VCardTestUriCreator {
   5604         private String mLookup1;
   5605         private String mLookup2;
   5606 
   5607         public VCardTestUriCreator(String lookup1, String lookup2) {
   5608             super();
   5609             mLookup1 = lookup1;
   5610             mLookup2 = lookup2;
   5611         }
   5612 
   5613         public Uri getUri1() {
   5614             return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup1);
   5615         }
   5616 
   5617         public Uri getUri2() {
   5618             return Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, mLookup2);
   5619         }
   5620 
   5621         public Uri getCombinedUri() {
   5622             return Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI,
   5623                     Uri.encode(mLookup1 + ":" + mLookup2));
   5624         }
   5625     }
   5626 
   5627     private VCardTestUriCreator createVCardTestContacts() {
   5628         final long rawContactId1 = createRawContact(mAccount, RawContacts.SOURCE_ID, "4:12");
   5629         insertStructuredName(rawContactId1, "John", "Doe");
   5630 
   5631         final long rawContactId2 = createRawContact(mAccount, RawContacts.SOURCE_ID, "3:4%121");
   5632         insertStructuredName(rawContactId2, "Jane", "Doh");
   5633 
   5634         final long contactId1 = queryContactId(rawContactId1);
   5635         final long contactId2 = queryContactId(rawContactId2);
   5636         final Uri contact1Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1);
   5637         final Uri contact2Uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId2);
   5638         final String lookup1 =
   5639             Uri.encode(Contacts.getLookupUri(mResolver, contact1Uri).getPathSegments().get(2));
   5640         final String lookup2 =
   5641             Uri.encode(Contacts.getLookupUri(mResolver, contact2Uri).getPathSegments().get(2));
   5642         return new VCardTestUriCreator(lookup1, lookup2);
   5643     }
   5644 
   5645     public void testQueryMultiVCard() {
   5646         // No need to create any contacts here, because the query for multiple vcards
   5647         // does not go into the database at all
   5648         Uri uri = Uri.withAppendedPath(Contacts.CONTENT_MULTI_VCARD_URI, Uri.encode("123:456"));
   5649         Cursor cursor = mResolver.query(uri, null, null, null, null);
   5650         assertEquals(1, cursor.getCount());
   5651         assertTrue(cursor.moveToFirst());
   5652         assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
   5653         String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
   5654 
   5655         // The resulting name contains date and time. Ensure that before and after are correct
   5656         assertTrue(filename.startsWith("vcards_"));
   5657         assertTrue(filename.endsWith(".vcf"));
   5658         cursor.close();
   5659     }
   5660 
   5661     public void testQueryFileSingleVCard() {
   5662         final VCardTestUriCreator contacts = createVCardTestContacts();
   5663 
   5664         {
   5665             Cursor cursor = mResolver.query(contacts.getUri1(), null, null, null, null);
   5666             assertEquals(1, cursor.getCount());
   5667             assertTrue(cursor.moveToFirst());
   5668             assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
   5669             String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
   5670             assertEquals("John Doe.vcf", filename);
   5671             cursor.close();
   5672         }
   5673 
   5674         {
   5675             Cursor cursor = mResolver.query(contacts.getUri2(), null, null, null, null);
   5676             assertEquals(1, cursor.getCount());
   5677             assertTrue(cursor.moveToFirst());
   5678             assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
   5679             String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
   5680             assertEquals("Jane Doh.vcf", filename);
   5681             cursor.close();
   5682         }
   5683     }
   5684 
   5685     public void testQueryFileProfileVCard() {
   5686         createBasicProfileContact(new ContentValues());
   5687         Cursor cursor = mResolver.query(Profile.CONTENT_VCARD_URI, null, null, null, null);
   5688         assertEquals(1, cursor.getCount());
   5689         assertTrue(cursor.moveToFirst());
   5690         assertTrue(cursor.isNull(cursor.getColumnIndex(OpenableColumns.SIZE)));
   5691         String filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
   5692         assertEquals("Mia Prophyl.vcf", filename);
   5693         cursor.close();
   5694     }
   5695 
   5696     public void testOpenAssetFileMultiVCard() throws IOException {
   5697         final VCardTestUriCreator contacts = createVCardTestContacts();
   5698 
   5699         final AssetFileDescriptor descriptor =
   5700             mResolver.openAssetFileDescriptor(contacts.getCombinedUri(), "r");
   5701         final FileInputStream inputStream = descriptor.createInputStream();
   5702         String data = readToEnd(inputStream);
   5703         inputStream.close();
   5704         descriptor.close();
   5705 
   5706         // Ensure that the resulting VCard has both contacts
   5707         assertTrue(data.contains("N:Doe;John;;;"));
   5708         assertTrue(data.contains("N:Doh;Jane;;;"));
   5709     }
   5710 
   5711     public void testOpenAssetFileSingleVCard() throws IOException {
   5712         final VCardTestUriCreator contacts = createVCardTestContacts();
   5713 
   5714         // Ensure that the right VCard is being created in each case
   5715         {
   5716             final AssetFileDescriptor descriptor =
   5717                 mResolver.openAssetFileDescriptor(contacts.getUri1(), "r");
   5718             final FileInputStream inputStream = descriptor.createInputStream();
   5719             final String data = readToEnd(inputStream);
   5720             inputStream.close();
   5721             descriptor.close();
   5722 
   5723             assertTrue(data.contains("N:Doe;John;;;"));
   5724             assertFalse(data.contains("N:Doh;Jane;;;"));
   5725         }
   5726 
   5727         {
   5728             final AssetFileDescriptor descriptor =
   5729                 mResolver.openAssetFileDescriptor(contacts.getUri2(), "r");
   5730             final FileInputStream inputStream = descriptor.createInputStream();
   5731             final String data = readToEnd(inputStream);
   5732             inputStream.close();
   5733             descriptor.close();
   5734 
   5735             assertFalse(data.contains("N:Doe;John;;;"));
   5736             assertTrue(data.contains("N:Doh;Jane;;;"));
   5737         }
   5738     }
   5739 
   5740     public void testAutoGroupMembership() {
   5741         long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */);
   5742         long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
   5743         long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false /* favorite */);
   5744         long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, false/* favorite */);
   5745         long r1 = createRawContact(mAccount);
   5746         long r2 = createRawContact(mAccountTwo);
   5747         long r3 = createRawContact(null);
   5748 
   5749         Cursor c = queryGroupMemberships(mAccount);
   5750         try {
   5751             assertTrue(c.moveToNext());
   5752             assertEquals(g1, c.getLong(0));
   5753             assertEquals(r1, c.getLong(1));
   5754             assertFalse(c.moveToNext());
   5755         } finally {
   5756             c.close();
   5757         }
   5758 
   5759         c = queryGroupMemberships(mAccountTwo);
   5760         try {
   5761             assertTrue(c.moveToNext());
   5762             assertEquals(g3, c.getLong(0));
   5763             assertEquals(r2, c.getLong(1));
   5764             assertFalse(c.moveToNext());
   5765         } finally {
   5766             c.close();
   5767         }
   5768     }
   5769 
   5770     public void testNoAutoAddMembershipAfterGroupCreation() {
   5771         long r1 = createRawContact(mAccount);
   5772         long r2 = createRawContact(mAccount);
   5773         long r3 = createRawContact(mAccount);
   5774         long r4 = createRawContact(mAccountTwo);
   5775         long r5 = createRawContact(mAccountTwo);
   5776         long r6 = createRawContact(null);
   5777 
   5778         assertNoRowsAndClose(queryGroupMemberships(mAccount));
   5779         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5780 
   5781         long g1 = createGroup(mAccount, "g1", "t1", 0, true /* autoAdd */, false /* favorite */);
   5782         long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
   5783         long g3 = createGroup(mAccountTwo, "g3", "t3", 0, true /* autoAdd */, false/* favorite */);
   5784 
   5785         assertNoRowsAndClose(queryGroupMemberships(mAccount));
   5786         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5787     }
   5788 
   5789     // create some starred and non-starred contacts, some associated with account, some not
   5790     // favorites group created
   5791     // the starred contacts should be added to group
   5792     // favorites group removed
   5793     // no change to starred status
   5794     public void testFavoritesMembershipAfterGroupCreation() {
   5795         long r1 = createRawContact(mAccount, RawContacts.STARRED, "1");
   5796         long r2 = createRawContact(mAccount);
   5797         long r3 = createRawContact(mAccount, RawContacts.STARRED, "1");
   5798         long r4 = createRawContact(mAccountTwo, RawContacts.STARRED, "1");
   5799         long r5 = createRawContact(mAccountTwo);
   5800         long r6 = createRawContact(null, RawContacts.STARRED, "1");
   5801         long r7 = createRawContact(null);
   5802 
   5803         assertNoRowsAndClose(queryGroupMemberships(mAccount));
   5804         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5805 
   5806         long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
   5807         long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false /* favorite */);
   5808         long g3 = createGroup(mAccountTwo, "g3", "t3", 0, false /* autoAdd */, false/* favorite */);
   5809 
   5810         assertTrue(queryRawContactIsStarred(r1));
   5811         assertFalse(queryRawContactIsStarred(r2));
   5812         assertTrue(queryRawContactIsStarred(r3));
   5813         assertTrue(queryRawContactIsStarred(r4));
   5814         assertFalse(queryRawContactIsStarred(r5));
   5815         assertTrue(queryRawContactIsStarred(r6));
   5816         assertFalse(queryRawContactIsStarred(r7));
   5817 
   5818         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5819         Cursor c = queryGroupMemberships(mAccount);
   5820         try {
   5821             assertTrue(c.moveToNext());
   5822             assertEquals(g1, c.getLong(0));
   5823             assertEquals(r1, c.getLong(1));
   5824             assertTrue(c.moveToNext());
   5825             assertEquals(g1, c.getLong(0));
   5826             assertEquals(r3, c.getLong(1));
   5827             assertFalse(c.moveToNext());
   5828         } finally {
   5829             c.close();
   5830         }
   5831 
   5832         updateItem(RawContacts.CONTENT_URI, r6,
   5833                 RawContacts.ACCOUNT_NAME, mAccount.name,
   5834                 RawContacts.ACCOUNT_TYPE, mAccount.type);
   5835         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5836         c = queryGroupMemberships(mAccount);
   5837         try {
   5838             assertTrue(c.moveToNext());
   5839             assertEquals(g1, c.getLong(0));
   5840             assertEquals(r1, c.getLong(1));
   5841             assertTrue(c.moveToNext());
   5842             assertEquals(g1, c.getLong(0));
   5843             assertEquals(r3, c.getLong(1));
   5844             assertTrue(c.moveToNext());
   5845             assertEquals(g1, c.getLong(0));
   5846             assertEquals(r6, c.getLong(1));
   5847             assertFalse(c.moveToNext());
   5848         } finally {
   5849             c.close();
   5850         }
   5851 
   5852         mResolver.delete(ContentUris.withAppendedId(Groups.CONTENT_URI, g1), null, null);
   5853 
   5854         assertNoRowsAndClose(queryGroupMemberships(mAccount));
   5855         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5856 
   5857         assertTrue(queryRawContactIsStarred(r1));
   5858         assertFalse(queryRawContactIsStarred(r2));
   5859         assertTrue(queryRawContactIsStarred(r3));
   5860         assertTrue(queryRawContactIsStarred(r4));
   5861         assertFalse(queryRawContactIsStarred(r5));
   5862         assertTrue(queryRawContactIsStarred(r6));
   5863         assertFalse(queryRawContactIsStarred(r7));
   5864     }
   5865 
   5866     public void testFavoritesGroupMembershipChangeAfterStarChange() {
   5867         long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
   5868         long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */);
   5869         long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */);
   5870         long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */);
   5871         long r1 = createRawContact(mAccount, RawContacts.STARRED, "1");
   5872         long r2 = createRawContact(mAccount);
   5873         long r3 = createRawContact(mAccountTwo);
   5874 
   5875         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5876         Cursor c = queryGroupMemberships(mAccount);
   5877         try {
   5878             assertTrue(c.moveToNext());
   5879             assertEquals(g1, c.getLong(0));
   5880             assertEquals(r1, c.getLong(1));
   5881             assertFalse(c.moveToNext());
   5882         } finally {
   5883             c.close();
   5884         }
   5885 
   5886         // remove the star from r1
   5887         assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0"));
   5888 
   5889         // Since no raw contacts are starred, there should be no group memberships.
   5890         assertNoRowsAndClose(queryGroupMemberships(mAccount));
   5891         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5892 
   5893         // mark r1 as starred
   5894         assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "1"));
   5895         // Now that r1 is starred it should have a membership in the one groups from mAccount
   5896         // that is marked as a favorite.
   5897         // There should be no memberships in mAccountTwo since it has no starred raw contacts.
   5898         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5899         c = queryGroupMemberships(mAccount);
   5900         try {
   5901             assertTrue(c.moveToNext());
   5902             assertEquals(g1, c.getLong(0));
   5903             assertEquals(r1, c.getLong(1));
   5904             assertFalse(c.moveToNext());
   5905         } finally {
   5906             c.close();
   5907         }
   5908 
   5909         // remove the star from r1
   5910         assertEquals(1, updateItem(RawContacts.CONTENT_URI, r1, RawContacts.STARRED, "0"));
   5911         // Since no raw contacts are starred, there should be no group memberships.
   5912         assertNoRowsAndClose(queryGroupMemberships(mAccount));
   5913         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5914 
   5915         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(r1));
   5916         assertNotNull(contactUri);
   5917 
   5918         // mark r1 as starred via its contact lookup uri
   5919         assertEquals(1, updateItem(contactUri, Contacts.STARRED, "1"));
   5920         // Now that r1 is starred it should have a membership in the one groups from mAccount
   5921         // that is marked as a favorite.
   5922         // There should be no memberships in mAccountTwo since it has no starred raw contacts.
   5923         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5924         c = queryGroupMemberships(mAccount);
   5925         try {
   5926             assertTrue(c.moveToNext());
   5927             assertEquals(g1, c.getLong(0));
   5928             assertEquals(r1, c.getLong(1));
   5929             assertFalse(c.moveToNext());
   5930         } finally {
   5931             c.close();
   5932         }
   5933 
   5934         // remove the star from r1
   5935         updateItem(contactUri, Contacts.STARRED, "0");
   5936         // Since no raw contacts are starred, there should be no group memberships.
   5937         assertNoRowsAndClose(queryGroupMemberships(mAccount));
   5938         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5939     }
   5940 
   5941     public void testStarChangedAfterGroupMembershipChange() {
   5942         long g1 = createGroup(mAccount, "g1", "t1", 0, false /* autoAdd */, true /* favorite */);
   5943         long g2 = createGroup(mAccount, "g2", "t2", 0, false /* autoAdd */, false/* favorite */);
   5944         long g4 = createGroup(mAccountTwo, "g4", "t4", 0, false /* autoAdd */, true /* favorite */);
   5945         long g5 = createGroup(mAccountTwo, "g5", "t5", 0, false /* autoAdd */, false/* favorite */);
   5946         long r1 = createRawContact(mAccount);
   5947         long r2 = createRawContact(mAccount);
   5948         long r3 = createRawContact(mAccountTwo);
   5949 
   5950         assertFalse(queryRawContactIsStarred(r1));
   5951         assertFalse(queryRawContactIsStarred(r2));
   5952         assertFalse(queryRawContactIsStarred(r3));
   5953 
   5954         Cursor c;
   5955 
   5956         // add r1 to one favorites group
   5957         // r1's star should automatically be set
   5958         // r1 should automatically be added to the other favorites group
   5959         Uri urir1g1 = insertGroupMembership(r1, g1);
   5960         assertTrue(queryRawContactIsStarred(r1));
   5961         assertFalse(queryRawContactIsStarred(r2));
   5962         assertFalse(queryRawContactIsStarred(r3));
   5963         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5964         c = queryGroupMemberships(mAccount);
   5965         try {
   5966             assertTrue(c.moveToNext());
   5967             assertEquals(g1, c.getLong(0));
   5968             assertEquals(r1, c.getLong(1));
   5969             assertFalse(c.moveToNext());
   5970         } finally {
   5971             c.close();
   5972         }
   5973 
   5974         // remove r1 from one favorites group
   5975         mResolver.delete(urir1g1, null, null);
   5976         // r1's star should no longer be set
   5977         assertFalse(queryRawContactIsStarred(r1));
   5978         assertFalse(queryRawContactIsStarred(r2));
   5979         assertFalse(queryRawContactIsStarred(r3));
   5980         // there should be no membership rows
   5981         assertNoRowsAndClose(queryGroupMemberships(mAccount));
   5982         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   5983 
   5984         // add r3 to the one favorites group for that account
   5985         // r3's star should automatically be set
   5986         Uri urir3g4 = insertGroupMembership(r3, g4);
   5987         assertFalse(queryRawContactIsStarred(r1));
   5988         assertFalse(queryRawContactIsStarred(r2));
   5989         assertTrue(queryRawContactIsStarred(r3));
   5990         assertNoRowsAndClose(queryGroupMemberships(mAccount));
   5991         c = queryGroupMemberships(mAccountTwo);
   5992         try {
   5993             assertTrue(c.moveToNext());
   5994             assertEquals(g4, c.getLong(0));
   5995             assertEquals(r3, c.getLong(1));
   5996             assertFalse(c.moveToNext());
   5997         } finally {
   5998             c.close();
   5999         }
   6000 
   6001         // remove r3 from the favorites group
   6002         mResolver.delete(urir3g4, null, null);
   6003         // r3's star should automatically be cleared
   6004         assertFalse(queryRawContactIsStarred(r1));
   6005         assertFalse(queryRawContactIsStarred(r2));
   6006         assertFalse(queryRawContactIsStarred(r3));
   6007         assertNoRowsAndClose(queryGroupMemberships(mAccount));
   6008         assertNoRowsAndClose(queryGroupMemberships(mAccountTwo));
   6009     }
   6010 
   6011     public void testReadOnlyRawContact() {
   6012         long rawContactId = createRawContact();
   6013         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
   6014         storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first");
   6015         storeValue(rawContactUri, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1);
   6016 
   6017         storeValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "second");
   6018         assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "first");
   6019 
   6020         Uri syncAdapterUri = rawContactUri.buildUpon()
   6021                 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1")
   6022                 .build();
   6023         storeValue(syncAdapterUri, RawContacts.CUSTOM_RINGTONE, "third");
   6024         assertStoredValue(rawContactUri, RawContacts.CUSTOM_RINGTONE, "third");
   6025     }
   6026 
   6027     public void testReadOnlyDataRow() {
   6028         long rawContactId = createRawContact();
   6029         Uri emailUri = insertEmail(rawContactId, "email");
   6030         Uri phoneUri = insertPhoneNumber(rawContactId, "555-1111");
   6031 
   6032         storeValue(emailUri, Data.IS_READ_ONLY, "1");
   6033         storeValue(emailUri, Email.ADDRESS, "changed");
   6034         storeValue(phoneUri, Phone.NUMBER, "555-2222");
   6035         assertStoredValue(emailUri, Email.ADDRESS, "email");
   6036         assertStoredValue(phoneUri, Phone.NUMBER, "555-2222");
   6037 
   6038         Uri syncAdapterUri = emailUri.buildUpon()
   6039                 .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "1")
   6040                 .build();
   6041         storeValue(syncAdapterUri, Email.ADDRESS, "changed");
   6042         assertStoredValue(emailUri, Email.ADDRESS, "changed");
   6043     }
   6044 
   6045     public void testContactWithReadOnlyRawContact() {
   6046         long rawContactId1 = createRawContact();
   6047         Uri rawContactUri1 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId1);
   6048         storeValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "first");
   6049 
   6050         long rawContactId2 = createRawContact();
   6051         Uri rawContactUri2 = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId2);
   6052         storeValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second");
   6053         storeValue(rawContactUri2, RawContacts.RAW_CONTACT_IS_READ_ONLY, 1);
   6054 
   6055         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
   6056                 rawContactId1, rawContactId2);
   6057 
   6058         long contactId = queryContactId(rawContactId1);
   6059 
   6060         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   6061         storeValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt");
   6062         assertStoredValue(contactUri, Contacts.CUSTOM_RINGTONE, "rt");
   6063         assertStoredValue(rawContactUri1, RawContacts.CUSTOM_RINGTONE, "rt");
   6064         assertStoredValue(rawContactUri2, RawContacts.CUSTOM_RINGTONE, "second");
   6065     }
   6066 
   6067     public void testNameParsingQuery() {
   6068         Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name")
   6069                 .appendQueryParameter(StructuredName.DISPLAY_NAME, "Mr. John Q. Doe Jr.").build();
   6070         Cursor cursor = mResolver.query(uri, null, null, null, null);
   6071         ContentValues values = new ContentValues();
   6072         values.put(StructuredName.DISPLAY_NAME, "Mr. John Q. Doe Jr.");
   6073         values.put(StructuredName.PREFIX, "Mr.");
   6074         values.put(StructuredName.GIVEN_NAME, "John");
   6075         values.put(StructuredName.MIDDLE_NAME, "Q.");
   6076         values.put(StructuredName.FAMILY_NAME, "Doe");
   6077         values.put(StructuredName.SUFFIX, "Jr.");
   6078         values.put(StructuredName.FULL_NAME_STYLE, FullNameStyle.WESTERN);
   6079         assertTrue(cursor.moveToFirst());
   6080         assertCursorValues(cursor, values);
   6081         cursor.close();
   6082     }
   6083 
   6084     public void testNameConcatenationQuery() {
   6085         Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name")
   6086                 .appendQueryParameter(StructuredName.PREFIX, "Mr")
   6087                 .appendQueryParameter(StructuredName.GIVEN_NAME, "John")
   6088                 .appendQueryParameter(StructuredName.MIDDLE_NAME, "Q.")
   6089                 .appendQueryParameter(StructuredName.FAMILY_NAME, "Doe")
   6090                 .appendQueryParameter(StructuredName.SUFFIX, "Jr.")
   6091                 .build();
   6092         Cursor cursor = mResolver.query(uri, null, null, null, null);
   6093         ContentValues values = new ContentValues();
   6094         values.put(StructuredName.DISPLAY_NAME, "Mr John Q. Doe, Jr.");
   6095         values.put(StructuredName.PREFIX, "Mr");
   6096         values.put(StructuredName.GIVEN_NAME, "John");
   6097         values.put(StructuredName.MIDDLE_NAME, "Q.");
   6098         values.put(StructuredName.FAMILY_NAME, "Doe");
   6099         values.put(StructuredName.SUFFIX, "Jr.");
   6100         values.put(StructuredName.FULL_NAME_STYLE, FullNameStyle.WESTERN);
   6101         assertTrue(cursor.moveToFirst());
   6102         assertCursorValues(cursor, values);
   6103         cursor.close();
   6104     }
   6105 
   6106     private Cursor queryGroupMemberships(Account account) {
   6107         Cursor c = mResolver.query(maybeAddAccountQueryParameters(Data.CONTENT_URI, account),
   6108                 new String[]{GroupMembership.GROUP_ROW_ID, GroupMembership.RAW_CONTACT_ID},
   6109                 Data.MIMETYPE + "=?", new String[]{GroupMembership.CONTENT_ITEM_TYPE},
   6110                 GroupMembership.GROUP_SOURCE_ID);
   6111         return c;
   6112     }
   6113 
   6114     private String readToEnd(FileInputStream inputStream) {
   6115         try {
   6116             System.out.println("DECLARED INPUT STREAM LENGTH: " + inputStream.available());
   6117             int ch;
   6118             StringBuilder stringBuilder = new StringBuilder();
   6119             int index = 0;
   6120             while (true) {
   6121                 ch = inputStream.read();
   6122                 System.out.println("READ CHARACTER: " + index + " " + ch);
   6123                 if (ch == -1) {
   6124                     break;
   6125                 }
   6126                 stringBuilder.append((char)ch);
   6127                 index++;
   6128             }
   6129             return stringBuilder.toString();
   6130         } catch (IOException e) {
   6131             return null;
   6132         }
   6133     }
   6134 
   6135     private void assertQueryParameter(String uriString, String parameter, String expectedValue) {
   6136         assertEquals(expectedValue, ContactsProvider2.getQueryParameter(
   6137                 Uri.parse(uriString), parameter));
   6138     }
   6139 
   6140     private long createContact(ContentValues values, String firstName, String givenName,
   6141             String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
   6142             long groupId, int chatMode) {
   6143         return createContact(values, firstName, givenName, phoneNumber, email, presenceStatus,
   6144                 timesContacted, starred, groupId, chatMode, false);
   6145     }
   6146 
   6147     private long createContact(ContentValues values, String firstName, String givenName,
   6148             String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
   6149             long groupId, int chatMode, boolean isUserProfile) {
   6150         return queryContactId(createRawContact(values, firstName, givenName, phoneNumber, email,
   6151                 presenceStatus, timesContacted, starred, groupId, chatMode, isUserProfile));
   6152     }
   6153 
   6154     private long createRawContact(ContentValues values, String firstName, String givenName,
   6155             String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
   6156             long groupId, int chatMode) {
   6157         long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus,
   6158                 timesContacted, starred, groupId, chatMode);
   6159         insertStructuredName(rawContactId, firstName, givenName);
   6160         return rawContactId;
   6161     }
   6162 
   6163     private long createRawContact(ContentValues values, String firstName, String givenName,
   6164             String phoneNumber, String email, int presenceStatus, int timesContacted, int starred,
   6165             long groupId, int chatMode, boolean isUserProfile) {
   6166         long rawContactId = createRawContact(values, phoneNumber, email, presenceStatus,
   6167                 timesContacted, starred, groupId, chatMode, isUserProfile);
   6168         insertStructuredName(rawContactId, firstName, givenName);
   6169         return rawContactId;
   6170     }
   6171 
   6172     private long createRawContact(ContentValues values, String phoneNumber, String email,
   6173             int presenceStatus, int timesContacted, int starred, long groupId, int chatMode) {
   6174         return createRawContact(values, phoneNumber, email, presenceStatus, timesContacted, starred,
   6175                 groupId, chatMode, false);
   6176     }
   6177 
   6178     private long createRawContact(ContentValues values, String phoneNumber, String email,
   6179             int presenceStatus, int timesContacted, int starred, long groupId, int chatMode,
   6180             boolean isUserProfile) {
   6181         values.put(RawContacts.STARRED, starred);
   6182         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   6183         values.put(RawContacts.CUSTOM_RINGTONE, "beethoven5");
   6184         values.put(RawContacts.TIMES_CONTACTED, timesContacted);
   6185 
   6186         Uri insertionUri = isUserProfile
   6187                 ? Profile.CONTENT_RAW_CONTACTS_URI
   6188                 : RawContacts.CONTENT_URI;
   6189         Uri rawContactUri = mResolver.insert(insertionUri, values);
   6190         long rawContactId = ContentUris.parseId(rawContactUri);
   6191         Uri photoUri = insertPhoto(rawContactId);
   6192         long photoId = ContentUris.parseId(photoUri);
   6193         values.put(Contacts.PHOTO_ID, photoId);
   6194         if (!TextUtils.isEmpty(phoneNumber)) {
   6195             insertPhoneNumber(rawContactId, phoneNumber);
   6196         }
   6197         if (!TextUtils.isEmpty(email)) {
   6198             insertEmail(rawContactId, email);
   6199         }
   6200 
   6201         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, email, presenceStatus, "hacking",
   6202                 chatMode, isUserProfile);
   6203 
   6204         if (groupId != 0) {
   6205             insertGroupMembership(rawContactId, groupId);
   6206         }
   6207 
   6208         return rawContactId;
   6209     }
   6210 
   6211     /**
   6212      * Creates a raw contact with pre-set values under the user's profile.
   6213      * @param profileValues Values to be used to create the entry (common values will be
   6214      *     automatically populated in createRawContact()).
   6215      * @return the raw contact ID that was created.
   6216      */
   6217     private long createBasicProfileContact(ContentValues profileValues) {
   6218         long profileRawContactId = createRawContact(profileValues, "Mia", "Prophyl",
   6219                 "18005554411", "mia.prophyl (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   6220                 StatusUpdates.CAPABILITY_HAS_CAMERA, true);
   6221         profileValues.put(Contacts.DISPLAY_NAME, "Mia Prophyl");
   6222         return profileRawContactId;
   6223     }
   6224 
   6225     /**
   6226      * Creates a raw contact with pre-set values that is not under the user's profile.
   6227      * @param nonProfileValues Values to be used to create the entry (common values will be
   6228      *     automatically populated in createRawContact()).
   6229      * @return the raw contact ID that was created.
   6230      */
   6231     private long createBasicNonProfileContact(ContentValues nonProfileValues) {
   6232         long nonProfileRawContactId = createRawContact(nonProfileValues, "John", "Doe",
   6233                 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   6234                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
   6235         nonProfileValues.put(Contacts.DISPLAY_NAME, "John Doe");
   6236         return nonProfileRawContactId;
   6237     }
   6238 
   6239     private void putDataValues(ContentValues values, long rawContactId) {
   6240         values.put(Data.RAW_CONTACT_ID, rawContactId);
   6241         values.put(Data.MIMETYPE, "testmimetype");
   6242         values.put(Data.RES_PACKAGE, "oldpackage");
   6243         values.put(Data.IS_PRIMARY, 1);
   6244         values.put(Data.IS_SUPER_PRIMARY, 1);
   6245         values.put(Data.DATA1, "one");
   6246         values.put(Data.DATA2, "two");
   6247         values.put(Data.DATA3, "three");
   6248         values.put(Data.DATA4, "four");
   6249         values.put(Data.DATA5, "five");
   6250         values.put(Data.DATA6, "six");
   6251         values.put(Data.DATA7, "seven");
   6252         values.put(Data.DATA8, "eight");
   6253         values.put(Data.DATA9, "nine");
   6254         values.put(Data.DATA10, "ten");
   6255         values.put(Data.DATA11, "eleven");
   6256         values.put(Data.DATA12, "twelve");
   6257         values.put(Data.DATA13, "thirteen");
   6258         values.put(Data.DATA14, "fourteen");
   6259         values.put(Data.DATA15, "fifteen");
   6260         values.put(Data.SYNC1, "sync1");
   6261         values.put(Data.SYNC2, "sync2");
   6262         values.put(Data.SYNC3, "sync3");
   6263         values.put(Data.SYNC4, "sync4");
   6264     }
   6265 
   6266     /**
   6267      * @param data1 email address or phone number
   6268      * @param usageType One of {@link DataUsageFeedback#USAGE_TYPE}
   6269      * @param values ContentValues for this feedback. Useful for incrementing
   6270      * {Contacts#TIMES_CONTACTED} in the ContentValue. Can be null.
   6271      */
   6272     private void sendFeedback(String data1, String usageType, ContentValues values) {
   6273         final long dataId = getStoredLongValue(Data.CONTENT_URI,
   6274                 Data.DATA1 + "=?", new String[] { data1 }, Data._ID);
   6275         final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
   6276                 .appendPath(String.valueOf(dataId))
   6277                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, usageType)
   6278                 .build();
   6279         assertNotSame(0, mResolver.update(feedbackUri, new ContentValues(), null, null));
   6280         if (values != null && values.containsKey(Contacts.TIMES_CONTACTED)) {
   6281             values.put(Contacts.TIMES_CONTACTED, values.getAsInteger(Contacts.TIMES_CONTACTED) + 1);
   6282         }
   6283     }
   6284 }
   6285