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