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