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