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