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.ContentProvider;
     23 import android.content.ContentProviderOperation;
     24 import android.content.ContentProviderResult;
     25 import android.content.ContentResolver;
     26 import android.content.ContentUris;
     27 import android.content.ContentValues;
     28 import android.content.Entity;
     29 import android.content.EntityIterator;
     30 import android.content.pm.UserInfo;
     31 import android.content.res.AssetFileDescriptor;
     32 import android.database.Cursor;
     33 import android.database.MatrixCursor;
     34 import android.database.sqlite.SQLiteDatabase;
     35 import android.net.Uri;
     36 import android.os.AsyncTask;
     37 import android.os.UserManager;
     38 import android.provider.CallLog.Calls;
     39 import android.provider.CallLog;
     40 import android.provider.ContactsContract;
     41 import android.provider.ContactsContract.AggregationExceptions;
     42 import android.provider.ContactsContract.CommonDataKinds.Callable;
     43 import android.provider.ContactsContract.CommonDataKinds.Contactables;
     44 import android.provider.ContactsContract.CommonDataKinds.Email;
     45 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
     46 import android.provider.ContactsContract.CommonDataKinds.Im;
     47 import android.provider.ContactsContract.CommonDataKinds.Organization;
     48 import android.provider.ContactsContract.CommonDataKinds.Phone;
     49 import android.provider.ContactsContract.CommonDataKinds.Photo;
     50 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
     51 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
     52 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
     53 import android.provider.ContactsContract.Contacts;
     54 import android.provider.ContactsContract.Data;
     55 import android.provider.ContactsContract.DataUsageFeedback;
     56 import android.provider.ContactsContract.Directory;
     57 import android.provider.ContactsContract.DisplayNameSources;
     58 import android.provider.ContactsContract.DisplayPhoto;
     59 import android.provider.ContactsContract.FullNameStyle;
     60 import android.provider.ContactsContract.Groups;
     61 import android.provider.ContactsContract.PhoneLookup;
     62 import android.provider.ContactsContract.PhoneticNameStyle;
     63 import android.provider.ContactsContract.PinnedPositions;
     64 import android.provider.ContactsContract.Profile;
     65 import android.provider.ContactsContract.ProviderStatus;
     66 import android.provider.ContactsContract.RawContacts;
     67 import android.provider.ContactsContract.RawContactsEntity;
     68 import android.provider.ContactsContract.SearchSnippets;
     69 import android.provider.ContactsContract.Settings;
     70 import android.provider.ContactsContract.StatusUpdates;
     71 import android.provider.ContactsContract.StreamItemPhotos;
     72 import android.provider.ContactsContract.StreamItems;
     73 import android.provider.OpenableColumns;
     74 import android.test.MoreAsserts;
     75 import android.test.suitebuilder.annotation.LargeTest;
     76 import android.text.TextUtils;
     77 
     78 import com.android.internal.util.ArrayUtils;
     79 import com.android.providers.contacts.CallLogProviderTest.TestCallLogProvider;
     80 import com.android.providers.contacts.ContactsActor.AlteringUserContext;
     81 import com.android.providers.contacts.ContactsActor.MockUserManager;
     82 import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns;
     83 import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
     84 import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns;
     85 import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties;
     86 import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
     87 import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
     88 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
     89 import com.android.providers.contacts.testutil.CommonDatabaseUtils;
     90 import com.android.providers.contacts.testutil.ContactUtil;
     91 import com.android.providers.contacts.testutil.DataUtil;
     92 import com.android.providers.contacts.testutil.DatabaseAsserts;
     93 import com.android.providers.contacts.testutil.DeletedContactUtil;
     94 import com.android.providers.contacts.testutil.RawContactUtil;
     95 import com.android.providers.contacts.testutil.TestUtil;
     96 import com.android.providers.contacts.tests.R;
     97 
     98 import com.google.android.collect.Lists;
     99 import com.google.android.collect.Sets;
    100 
    101 import java.io.FileInputStream;
    102 import java.io.IOException;
    103 import java.io.OutputStream;
    104 import java.text.Collator;
    105 import java.util.ArrayList;
    106 import java.util.Arrays;
    107 import java.util.HashSet;
    108 import java.util.List;
    109 import java.util.Locale;
    110 import java.util.Set;
    111 
    112 /**
    113  * Unit tests for {@link ContactsProvider2}.
    114  *
    115  * Run the test like this:
    116  * <code>
    117    adb shell am instrument -e class com.android.providers.contacts.ContactsProvider2Test -w \
    118            com.android.providers.contacts.tests/android.test.InstrumentationTestRunner
    119  * </code>
    120  */
    121 @LargeTest
    122 public class ContactsProvider2Test extends BaseContactsProvider2Test {
    123 
    124     private static final String TAG = ContactsProvider2Test.class.getSimpleName();
    125 
    126     public void testContactsProjection() {
    127         assertProjection(Contacts.CONTENT_URI, new String[]{
    128                 Contacts._ID,
    129                 Contacts.DISPLAY_NAME_PRIMARY,
    130                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    131                 Contacts.DISPLAY_NAME_SOURCE,
    132                 Contacts.PHONETIC_NAME,
    133                 Contacts.PHONETIC_NAME_STYLE,
    134                 Contacts.SORT_KEY_PRIMARY,
    135                 Contacts.SORT_KEY_ALTERNATIVE,
    136                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
    137                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
    138                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
    139                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
    140                 Contacts.LAST_TIME_CONTACTED,
    141                 Contacts.TIMES_CONTACTED,
    142                 Contacts.STARRED,
    143                 Contacts.PINNED,
    144                 Contacts.IN_DEFAULT_DIRECTORY,
    145                 Contacts.IN_VISIBLE_GROUP,
    146                 Contacts.PHOTO_ID,
    147                 Contacts.PHOTO_FILE_ID,
    148                 Contacts.PHOTO_URI,
    149                 Contacts.PHOTO_THUMBNAIL_URI,
    150                 Contacts.CUSTOM_RINGTONE,
    151                 Contacts.HAS_PHONE_NUMBER,
    152                 Contacts.SEND_TO_VOICEMAIL,
    153                 Contacts.IS_USER_PROFILE,
    154                 Contacts.LOOKUP_KEY,
    155                 Contacts.NAME_RAW_CONTACT_ID,
    156                 Contacts.CONTACT_PRESENCE,
    157                 Contacts.CONTACT_CHAT_CAPABILITY,
    158                 Contacts.CONTACT_STATUS,
    159                 Contacts.CONTACT_STATUS_TIMESTAMP,
    160                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    161                 Contacts.CONTACT_STATUS_LABEL,
    162                 Contacts.CONTACT_STATUS_ICON,
    163                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP
    164         });
    165     }
    166 
    167     public void testContactsStrequentProjection() {
    168         assertProjection(Contacts.CONTENT_STREQUENT_URI, new String[]{
    169                 Contacts._ID,
    170                 Contacts.DISPLAY_NAME_PRIMARY,
    171                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    172                 Contacts.DISPLAY_NAME_SOURCE,
    173                 Contacts.PHONETIC_NAME,
    174                 Contacts.PHONETIC_NAME_STYLE,
    175                 Contacts.SORT_KEY_PRIMARY,
    176                 Contacts.SORT_KEY_ALTERNATIVE,
    177                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
    178                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
    179                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
    180                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
    181                 Contacts.LAST_TIME_CONTACTED,
    182                 Contacts.TIMES_CONTACTED,
    183                 Contacts.STARRED,
    184                 Contacts.PINNED,
    185                 Contacts.IN_DEFAULT_DIRECTORY,
    186                 Contacts.IN_VISIBLE_GROUP,
    187                 Contacts.PHOTO_ID,
    188                 Contacts.PHOTO_FILE_ID,
    189                 Contacts.PHOTO_URI,
    190                 Contacts.PHOTO_THUMBNAIL_URI,
    191                 Contacts.CUSTOM_RINGTONE,
    192                 Contacts.HAS_PHONE_NUMBER,
    193                 Contacts.SEND_TO_VOICEMAIL,
    194                 Contacts.IS_USER_PROFILE,
    195                 Contacts.LOOKUP_KEY,
    196                 Contacts.NAME_RAW_CONTACT_ID,
    197                 Contacts.CONTACT_PRESENCE,
    198                 Contacts.CONTACT_CHAT_CAPABILITY,
    199                 Contacts.CONTACT_STATUS,
    200                 Contacts.CONTACT_STATUS_TIMESTAMP,
    201                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    202                 Contacts.CONTACT_STATUS_LABEL,
    203                 Contacts.CONTACT_STATUS_ICON,
    204                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
    205                 DataUsageStatColumns.TIMES_USED,
    206                 DataUsageStatColumns.LAST_TIME_USED,
    207         });
    208     }
    209 
    210     public void testContactsStrequentPhoneOnlyProjection() {
    211         assertProjection(Contacts.CONTENT_STREQUENT_URI.buildUpon()
    212                     .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build(),
    213                 new String[] {
    214                 Contacts._ID,
    215                 Contacts.DISPLAY_NAME_PRIMARY,
    216                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    217                 Contacts.DISPLAY_NAME_SOURCE,
    218                 Contacts.PHONETIC_NAME,
    219                 Contacts.PHONETIC_NAME_STYLE,
    220                 Contacts.SORT_KEY_PRIMARY,
    221                 Contacts.SORT_KEY_ALTERNATIVE,
    222                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
    223                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
    224                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
    225                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
    226                 Contacts.LAST_TIME_CONTACTED,
    227                 Contacts.TIMES_CONTACTED,
    228                 Contacts.STARRED,
    229                 Contacts.PINNED,
    230                 Contacts.IN_DEFAULT_DIRECTORY,
    231                 Contacts.IN_VISIBLE_GROUP,
    232                 Contacts.PHOTO_ID,
    233                 Contacts.PHOTO_FILE_ID,
    234                 Contacts.PHOTO_URI,
    235                 Contacts.PHOTO_THUMBNAIL_URI,
    236                 Contacts.CUSTOM_RINGTONE,
    237                 Contacts.HAS_PHONE_NUMBER,
    238                 Contacts.SEND_TO_VOICEMAIL,
    239                 Contacts.IS_USER_PROFILE,
    240                 Contacts.LOOKUP_KEY,
    241                 Contacts.NAME_RAW_CONTACT_ID,
    242                 Contacts.CONTACT_PRESENCE,
    243                 Contacts.CONTACT_CHAT_CAPABILITY,
    244                 Contacts.CONTACT_STATUS,
    245                 Contacts.CONTACT_STATUS_TIMESTAMP,
    246                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    247                 Contacts.CONTACT_STATUS_LABEL,
    248                 Contacts.CONTACT_STATUS_ICON,
    249                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
    250                 DataUsageStatColumns.TIMES_USED,
    251                 DataUsageStatColumns.LAST_TIME_USED,
    252                 Phone.NUMBER,
    253                 Phone.TYPE,
    254                 Phone.LABEL,
    255                 Phone.IS_SUPER_PRIMARY,
    256                 Phone.CONTACT_ID
    257         });
    258     }
    259 
    260     public void testContactsWithSnippetProjection() {
    261         assertProjection(Contacts.CONTENT_FILTER_URI.buildUpon().appendPath("nothing").build(),
    262             new String[]{
    263                 Contacts._ID,
    264                 Contacts.DISPLAY_NAME_PRIMARY,
    265                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    266                 Contacts.DISPLAY_NAME_SOURCE,
    267                 Contacts.PHONETIC_NAME,
    268                 Contacts.PHONETIC_NAME_STYLE,
    269                 Contacts.SORT_KEY_PRIMARY,
    270                 Contacts.SORT_KEY_ALTERNATIVE,
    271                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
    272                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
    273                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
    274                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
    275                 Contacts.LAST_TIME_CONTACTED,
    276                 Contacts.TIMES_CONTACTED,
    277                 Contacts.STARRED,
    278                 Contacts.PINNED,
    279                 Contacts.IN_DEFAULT_DIRECTORY,
    280                 Contacts.IN_VISIBLE_GROUP,
    281                 Contacts.PHOTO_ID,
    282                 Contacts.PHOTO_FILE_ID,
    283                 Contacts.PHOTO_URI,
    284                 Contacts.PHOTO_THUMBNAIL_URI,
    285                 Contacts.CUSTOM_RINGTONE,
    286                 Contacts.HAS_PHONE_NUMBER,
    287                 Contacts.SEND_TO_VOICEMAIL,
    288                 Contacts.IS_USER_PROFILE,
    289                 Contacts.LOOKUP_KEY,
    290                 Contacts.NAME_RAW_CONTACT_ID,
    291                 Contacts.CONTACT_PRESENCE,
    292                 Contacts.CONTACT_CHAT_CAPABILITY,
    293                 Contacts.CONTACT_STATUS,
    294                 Contacts.CONTACT_STATUS_TIMESTAMP,
    295                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    296                 Contacts.CONTACT_STATUS_LABEL,
    297                 Contacts.CONTACT_STATUS_ICON,
    298                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
    299                 SearchSnippets.SNIPPET,
    300         });
    301     }
    302 
    303     public void testRawContactsProjection() {
    304         assertProjection(RawContacts.CONTENT_URI, new String[]{
    305                 RawContacts._ID,
    306                 RawContacts.CONTACT_ID,
    307                 RawContacts.ACCOUNT_NAME,
    308                 RawContacts.ACCOUNT_TYPE,
    309                 RawContacts.DATA_SET,
    310                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
    311                 RawContacts.SOURCE_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.NAME_VERIFIED,
    322                 RawContacts.SORT_KEY_PRIMARY,
    323                 RawContacts.SORT_KEY_ALTERNATIVE,
    324                 RawContactsColumns.PHONEBOOK_LABEL_PRIMARY,
    325                 RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
    326                 RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
    327                 RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
    328                 RawContacts.TIMES_CONTACTED,
    329                 RawContacts.LAST_TIME_CONTACTED,
    330                 RawContacts.CUSTOM_RINGTONE,
    331                 RawContacts.SEND_TO_VOICEMAIL,
    332                 RawContacts.STARRED,
    333                 RawContacts.PINNED,
    334                 RawContacts.AGGREGATION_MODE,
    335                 RawContacts.SYNC1,
    336                 RawContacts.SYNC2,
    337                 RawContacts.SYNC3,
    338                 RawContacts.SYNC4,
    339         });
    340     }
    341 
    342     public void testDataProjection() {
    343         assertProjection(Data.CONTENT_URI, new String[]{
    344                 Data._ID,
    345                 Data.RAW_CONTACT_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.SYNC1,
    367                 Data.SYNC2,
    368                 Data.SYNC3,
    369                 Data.SYNC4,
    370                 Data.CONTACT_ID,
    371                 Data.PRESENCE,
    372                 Data.CHAT_CAPABILITY,
    373                 Data.STATUS,
    374                 Data.STATUS_TIMESTAMP,
    375                 Data.STATUS_RES_PACKAGE,
    376                 Data.STATUS_LABEL,
    377                 Data.STATUS_ICON,
    378                 Data.TIMES_USED,
    379                 Data.LAST_TIME_USED,
    380                 RawContacts.ACCOUNT_NAME,
    381                 RawContacts.ACCOUNT_TYPE,
    382                 RawContacts.DATA_SET,
    383                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
    384                 RawContacts.SOURCE_ID,
    385                 RawContacts.VERSION,
    386                 RawContacts.DIRTY,
    387                 RawContacts.NAME_VERIFIED,
    388                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
    389                 Contacts._ID,
    390                 Contacts.DISPLAY_NAME_PRIMARY,
    391                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    392                 Contacts.DISPLAY_NAME_SOURCE,
    393                 Contacts.PHONETIC_NAME,
    394                 Contacts.PHONETIC_NAME_STYLE,
    395                 Contacts.SORT_KEY_PRIMARY,
    396                 Contacts.SORT_KEY_ALTERNATIVE,
    397                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
    398                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
    399                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
    400                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
    401                 Contacts.LAST_TIME_CONTACTED,
    402                 Contacts.TIMES_CONTACTED,
    403                 Contacts.STARRED,
    404                 Contacts.PINNED,
    405                 Contacts.IN_DEFAULT_DIRECTORY,
    406                 Contacts.IN_VISIBLE_GROUP,
    407                 Contacts.PHOTO_ID,
    408                 Contacts.PHOTO_FILE_ID,
    409                 Contacts.PHOTO_URI,
    410                 Contacts.PHOTO_THUMBNAIL_URI,
    411                 Contacts.CUSTOM_RINGTONE,
    412                 Contacts.SEND_TO_VOICEMAIL,
    413                 Contacts.LOOKUP_KEY,
    414                 Contacts.NAME_RAW_CONTACT_ID,
    415                 Contacts.HAS_PHONE_NUMBER,
    416                 Contacts.CONTACT_PRESENCE,
    417                 Contacts.CONTACT_CHAT_CAPABILITY,
    418                 Contacts.CONTACT_STATUS,
    419                 Contacts.CONTACT_STATUS_TIMESTAMP,
    420                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    421                 Contacts.CONTACT_STATUS_LABEL,
    422                 Contacts.CONTACT_STATUS_ICON,
    423                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
    424                 GroupMembership.GROUP_SOURCE_ID,
    425         });
    426     }
    427 
    428     public void testDistinctDataProjection() {
    429         assertProjection(Phone.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(),
    430             new String[]{
    431                 Data._ID,
    432                 Data.DATA_VERSION,
    433                 Data.IS_PRIMARY,
    434                 Data.IS_SUPER_PRIMARY,
    435                 Data.RES_PACKAGE,
    436                 Data.MIMETYPE,
    437                 Data.DATA1,
    438                 Data.DATA2,
    439                 Data.DATA3,
    440                 Data.DATA4,
    441                 Data.DATA5,
    442                 Data.DATA6,
    443                 Data.DATA7,
    444                 Data.DATA8,
    445                 Data.DATA9,
    446                 Data.DATA10,
    447                 Data.DATA11,
    448                 Data.DATA12,
    449                 Data.DATA13,
    450                 Data.DATA14,
    451                 Data.DATA15,
    452                 Data.SYNC1,
    453                 Data.SYNC2,
    454                 Data.SYNC3,
    455                 Data.SYNC4,
    456                 Data.CONTACT_ID,
    457                 Data.PRESENCE,
    458                 Data.CHAT_CAPABILITY,
    459                 Data.STATUS,
    460                 Data.STATUS_TIMESTAMP,
    461                 Data.STATUS_RES_PACKAGE,
    462                 Data.STATUS_LABEL,
    463                 Data.STATUS_ICON,
    464                 Data.TIMES_USED,
    465                 Data.LAST_TIME_USED,
    466                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
    467                 Contacts._ID,
    468                 Contacts.DISPLAY_NAME_PRIMARY,
    469                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    470                 Contacts.DISPLAY_NAME_SOURCE,
    471                 Contacts.PHONETIC_NAME,
    472                 Contacts.PHONETIC_NAME_STYLE,
    473                 Contacts.SORT_KEY_PRIMARY,
    474                 Contacts.SORT_KEY_ALTERNATIVE,
    475                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
    476                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
    477                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
    478                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
    479                 Contacts.LAST_TIME_CONTACTED,
    480                 Contacts.TIMES_CONTACTED,
    481                 Contacts.STARRED,
    482                 Contacts.PINNED,
    483                 Contacts.IN_DEFAULT_DIRECTORY,
    484                 Contacts.IN_VISIBLE_GROUP,
    485                 Contacts.PHOTO_ID,
    486                 Contacts.PHOTO_FILE_ID,
    487                 Contacts.PHOTO_URI,
    488                 Contacts.PHOTO_THUMBNAIL_URI,
    489                 Contacts.HAS_PHONE_NUMBER,
    490                 Contacts.CUSTOM_RINGTONE,
    491                 Contacts.SEND_TO_VOICEMAIL,
    492                 Contacts.LOOKUP_KEY,
    493                 Contacts.CONTACT_PRESENCE,
    494                 Contacts.CONTACT_CHAT_CAPABILITY,
    495                 Contacts.CONTACT_STATUS,
    496                 Contacts.CONTACT_STATUS_TIMESTAMP,
    497                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    498                 Contacts.CONTACT_STATUS_LABEL,
    499                 Contacts.CONTACT_STATUS_ICON,
    500                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
    501                 GroupMembership.GROUP_SOURCE_ID,
    502         });
    503     }
    504 
    505     public void testEntityProjection() {
    506         assertProjection(
    507             Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, 0),
    508                     Contacts.Entity.CONTENT_DIRECTORY),
    509             new String[]{
    510                 Contacts.Entity._ID,
    511                 Contacts.Entity.DATA_ID,
    512                 Contacts.Entity.RAW_CONTACT_ID,
    513                 Data.DATA_VERSION,
    514                 Data.IS_PRIMARY,
    515                 Data.IS_SUPER_PRIMARY,
    516                 Data.RES_PACKAGE,
    517                 Data.MIMETYPE,
    518                 Data.DATA1,
    519                 Data.DATA2,
    520                 Data.DATA3,
    521                 Data.DATA4,
    522                 Data.DATA5,
    523                 Data.DATA6,
    524                 Data.DATA7,
    525                 Data.DATA8,
    526                 Data.DATA9,
    527                 Data.DATA10,
    528                 Data.DATA11,
    529                 Data.DATA12,
    530                 Data.DATA13,
    531                 Data.DATA14,
    532                 Data.DATA15,
    533                 Data.SYNC1,
    534                 Data.SYNC2,
    535                 Data.SYNC3,
    536                 Data.SYNC4,
    537                 Data.CONTACT_ID,
    538                 Data.PRESENCE,
    539                 Data.CHAT_CAPABILITY,
    540                 Data.STATUS,
    541                 Data.STATUS_TIMESTAMP,
    542                 Data.STATUS_RES_PACKAGE,
    543                 Data.STATUS_LABEL,
    544                 Data.STATUS_ICON,
    545                 RawContacts.ACCOUNT_NAME,
    546                 RawContacts.ACCOUNT_TYPE,
    547                 RawContacts.DATA_SET,
    548                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
    549                 RawContacts.SOURCE_ID,
    550                 RawContacts.VERSION,
    551                 RawContacts.DELETED,
    552                 RawContacts.DIRTY,
    553                 RawContacts.NAME_VERIFIED,
    554                 RawContacts.SYNC1,
    555                 RawContacts.SYNC2,
    556                 RawContacts.SYNC3,
    557                 RawContacts.SYNC4,
    558                 Contacts._ID,
    559                 Contacts.DISPLAY_NAME_PRIMARY,
    560                 Contacts.DISPLAY_NAME_ALTERNATIVE,
    561                 Contacts.DISPLAY_NAME_SOURCE,
    562                 Contacts.PHONETIC_NAME,
    563                 Contacts.PHONETIC_NAME_STYLE,
    564                 Contacts.SORT_KEY_PRIMARY,
    565                 Contacts.SORT_KEY_ALTERNATIVE,
    566                 ContactsColumns.PHONEBOOK_LABEL_PRIMARY,
    567                 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
    568                 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE,
    569                 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
    570                 Contacts.LAST_TIME_CONTACTED,
    571                 Contacts.TIMES_CONTACTED,
    572                 Contacts.STARRED,
    573                 Contacts.PINNED,
    574                 Contacts.IN_DEFAULT_DIRECTORY,
    575                 Contacts.IN_VISIBLE_GROUP,
    576                 Contacts.PHOTO_ID,
    577                 Contacts.PHOTO_FILE_ID,
    578                 Contacts.PHOTO_URI,
    579                 Contacts.PHOTO_THUMBNAIL_URI,
    580                 Contacts.CUSTOM_RINGTONE,
    581                 Contacts.SEND_TO_VOICEMAIL,
    582                 Contacts.IS_USER_PROFILE,
    583                 Contacts.LOOKUP_KEY,
    584                 Contacts.NAME_RAW_CONTACT_ID,
    585                 Contacts.HAS_PHONE_NUMBER,
    586                 Contacts.CONTACT_PRESENCE,
    587                 Contacts.CONTACT_CHAT_CAPABILITY,
    588                 Contacts.CONTACT_STATUS,
    589                 Contacts.CONTACT_STATUS_TIMESTAMP,
    590                 Contacts.CONTACT_STATUS_RES_PACKAGE,
    591                 Contacts.CONTACT_STATUS_LABEL,
    592                 Contacts.CONTACT_STATUS_ICON,
    593                 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
    594                 GroupMembership.GROUP_SOURCE_ID,
    595                 DataUsageStatColumns.TIMES_USED,
    596                 DataUsageStatColumns.LAST_TIME_USED,
    597         });
    598     }
    599 
    600     public void testRawEntityProjection() {
    601         assertProjection(RawContactsEntity.CONTENT_URI, new String[]{
    602                 RawContacts.Entity.DATA_ID,
    603                 RawContacts._ID,
    604                 RawContacts.CONTACT_ID,
    605                 RawContacts.ACCOUNT_NAME,
    606                 RawContacts.ACCOUNT_TYPE,
    607                 RawContacts.DATA_SET,
    608                 RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
    609                 RawContacts.SOURCE_ID,
    610                 RawContacts.VERSION,
    611                 RawContacts.DIRTY,
    612                 RawContacts.NAME_VERIFIED,
    613                 RawContacts.DELETED,
    614                 RawContacts.SYNC1,
    615                 RawContacts.SYNC2,
    616                 RawContacts.SYNC3,
    617                 RawContacts.SYNC4,
    618                 RawContacts.STARRED,
    619                 RawContacts.RAW_CONTACT_IS_USER_PROFILE,
    620                 Data.DATA_VERSION,
    621                 Data.IS_PRIMARY,
    622                 Data.IS_SUPER_PRIMARY,
    623                 Data.RES_PACKAGE,
    624                 Data.MIMETYPE,
    625                 Data.DATA1,
    626                 Data.DATA2,
    627                 Data.DATA3,
    628                 Data.DATA4,
    629                 Data.DATA5,
    630                 Data.DATA6,
    631                 Data.DATA7,
    632                 Data.DATA8,
    633                 Data.DATA9,
    634                 Data.DATA10,
    635                 Data.DATA11,
    636                 Data.DATA12,
    637                 Data.DATA13,
    638                 Data.DATA14,
    639                 Data.DATA15,
    640                 Data.SYNC1,
    641                 Data.SYNC2,
    642                 Data.SYNC3,
    643                 Data.SYNC4,
    644                 GroupMembership.GROUP_SOURCE_ID,
    645         });
    646     }
    647 
    648     public void testPhoneLookupProjection() {
    649         assertProjection(PhoneLookup.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(),
    650             new String[]{
    651                 PhoneLookup._ID,
    652                 PhoneLookup.LOOKUP_KEY,
    653                 PhoneLookup.DISPLAY_NAME,
    654                 PhoneLookup.LAST_TIME_CONTACTED,
    655                 PhoneLookup.TIMES_CONTACTED,
    656                 PhoneLookup.STARRED,
    657                 PhoneLookup.IN_DEFAULT_DIRECTORY,
    658                 PhoneLookup.IN_VISIBLE_GROUP,
    659                 PhoneLookup.PHOTO_FILE_ID,
    660                 PhoneLookup.PHOTO_ID,
    661                 PhoneLookup.PHOTO_URI,
    662                 PhoneLookup.PHOTO_THUMBNAIL_URI,
    663                 PhoneLookup.CUSTOM_RINGTONE,
    664                 PhoneLookup.HAS_PHONE_NUMBER,
    665                 PhoneLookup.SEND_TO_VOICEMAIL,
    666                 PhoneLookup.NUMBER,
    667                 PhoneLookup.TYPE,
    668                 PhoneLookup.LABEL,
    669                 PhoneLookup.NORMALIZED_NUMBER,
    670         });
    671     }
    672 
    673     public void testPhoneLookupEnterpriseProjection() {
    674         assertProjection(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI
    675                         .buildUpon().appendPath("123").build(),
    676                 new String[]{
    677                         PhoneLookup._ID,
    678                         PhoneLookup.LOOKUP_KEY,
    679                         PhoneLookup.DISPLAY_NAME,
    680                         PhoneLookup.LAST_TIME_CONTACTED,
    681                         PhoneLookup.TIMES_CONTACTED,
    682                         PhoneLookup.STARRED,
    683                         PhoneLookup.IN_DEFAULT_DIRECTORY,
    684                         PhoneLookup.IN_VISIBLE_GROUP,
    685                         PhoneLookup.PHOTO_FILE_ID,
    686                         PhoneLookup.PHOTO_ID,
    687                         PhoneLookup.PHOTO_URI,
    688                         PhoneLookup.PHOTO_THUMBNAIL_URI,
    689                         PhoneLookup.CUSTOM_RINGTONE,
    690                         PhoneLookup.HAS_PHONE_NUMBER,
    691                         PhoneLookup.SEND_TO_VOICEMAIL,
    692                         PhoneLookup.NUMBER,
    693                         PhoneLookup.TYPE,
    694                         PhoneLookup.LABEL,
    695                         PhoneLookup.NORMALIZED_NUMBER,
    696                 });
    697     }
    698 
    699     public void testGroupsProjection() {
    700         assertProjection(Groups.CONTENT_URI, new String[]{
    701                 Groups._ID,
    702                 Groups.ACCOUNT_NAME,
    703                 Groups.ACCOUNT_TYPE,
    704                 Groups.DATA_SET,
    705                 Groups.ACCOUNT_TYPE_AND_DATA_SET,
    706                 Groups.SOURCE_ID,
    707                 Groups.DIRTY,
    708                 Groups.VERSION,
    709                 Groups.RES_PACKAGE,
    710                 Groups.TITLE,
    711                 Groups.TITLE_RES,
    712                 Groups.GROUP_VISIBLE,
    713                 Groups.SYSTEM_ID,
    714                 Groups.DELETED,
    715                 Groups.NOTES,
    716                 Groups.SHOULD_SYNC,
    717                 Groups.FAVORITES,
    718                 Groups.AUTO_ADD,
    719                 Groups.GROUP_IS_READ_ONLY,
    720                 Groups.SYNC1,
    721                 Groups.SYNC2,
    722                 Groups.SYNC3,
    723                 Groups.SYNC4,
    724         });
    725     }
    726 
    727     public void testGroupsSummaryProjection() {
    728         assertProjection(Groups.CONTENT_SUMMARY_URI, new String[]{
    729                 Groups._ID,
    730                 Groups.ACCOUNT_NAME,
    731                 Groups.ACCOUNT_TYPE,
    732                 Groups.DATA_SET,
    733                 Groups.ACCOUNT_TYPE_AND_DATA_SET,
    734                 Groups.SOURCE_ID,
    735                 Groups.DIRTY,
    736                 Groups.VERSION,
    737                 Groups.RES_PACKAGE,
    738                 Groups.TITLE,
    739                 Groups.TITLE_RES,
    740                 Groups.GROUP_VISIBLE,
    741                 Groups.SYSTEM_ID,
    742                 Groups.DELETED,
    743                 Groups.NOTES,
    744                 Groups.SHOULD_SYNC,
    745                 Groups.FAVORITES,
    746                 Groups.AUTO_ADD,
    747                 Groups.GROUP_IS_READ_ONLY,
    748                 Groups.SYNC1,
    749                 Groups.SYNC2,
    750                 Groups.SYNC3,
    751                 Groups.SYNC4,
    752                 Groups.SUMMARY_COUNT,
    753                 Groups.SUMMARY_WITH_PHONES,
    754                 Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT,
    755         });
    756     }
    757 
    758     public void testAggregateExceptionProjection() {
    759         assertProjection(AggregationExceptions.CONTENT_URI, new String[]{
    760                 AggregationExceptionColumns._ID,
    761                 AggregationExceptions.TYPE,
    762                 AggregationExceptions.RAW_CONTACT_ID1,
    763                 AggregationExceptions.RAW_CONTACT_ID2,
    764         });
    765     }
    766 
    767     public void testSettingsProjection() {
    768         assertProjection(Settings.CONTENT_URI, new String[]{
    769                 Settings.ACCOUNT_NAME,
    770                 Settings.ACCOUNT_TYPE,
    771                 Settings.DATA_SET,
    772                 Settings.UNGROUPED_VISIBLE,
    773                 Settings.SHOULD_SYNC,
    774                 Settings.ANY_UNSYNCED,
    775                 Settings.UNGROUPED_COUNT,
    776                 Settings.UNGROUPED_WITH_PHONES,
    777         });
    778     }
    779 
    780     public void testStatusUpdatesProjection() {
    781         assertProjection(StatusUpdates.CONTENT_URI, new String[]{
    782                 PresenceColumns.RAW_CONTACT_ID,
    783                 StatusUpdates.DATA_ID,
    784                 StatusUpdates.IM_ACCOUNT,
    785                 StatusUpdates.IM_HANDLE,
    786                 StatusUpdates.PROTOCOL,
    787                 StatusUpdates.CUSTOM_PROTOCOL,
    788                 StatusUpdates.PRESENCE,
    789                 StatusUpdates.CHAT_CAPABILITY,
    790                 StatusUpdates.STATUS,
    791                 StatusUpdates.STATUS_TIMESTAMP,
    792                 StatusUpdates.STATUS_RES_PACKAGE,
    793                 StatusUpdates.STATUS_ICON,
    794                 StatusUpdates.STATUS_LABEL,
    795         });
    796     }
    797 
    798     public void testDirectoryProjection() {
    799         assertProjection(Directory.CONTENT_URI, new String[]{
    800                 Directory._ID,
    801                 Directory.PACKAGE_NAME,
    802                 Directory.TYPE_RESOURCE_ID,
    803                 Directory.DISPLAY_NAME,
    804                 Directory.DIRECTORY_AUTHORITY,
    805                 Directory.ACCOUNT_TYPE,
    806                 Directory.ACCOUNT_NAME,
    807                 Directory.EXPORT_SUPPORT,
    808                 Directory.SHORTCUT_SUPPORT,
    809                 Directory.PHOTO_SUPPORT,
    810         });
    811     }
    812 
    813     public void testRawContactsInsert() {
    814         ContentValues values = new ContentValues();
    815 
    816         values.put(RawContacts.ACCOUNT_NAME, "a");
    817         values.put(RawContacts.ACCOUNT_TYPE, "b");
    818         values.put(RawContacts.DATA_SET, "ds");
    819         values.put(RawContacts.SOURCE_ID, "c");
    820         values.put(RawContacts.VERSION, 42);
    821         values.put(RawContacts.DIRTY, 1);
    822         values.put(RawContacts.DELETED, 1);
    823         values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
    824         values.put(RawContacts.CUSTOM_RINGTONE, "d");
    825         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
    826         values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
    827         values.put(RawContacts.STARRED, 1);
    828         values.put(RawContacts.SYNC1, "e");
    829         values.put(RawContacts.SYNC2, "f");
    830         values.put(RawContacts.SYNC3, "g");
    831         values.put(RawContacts.SYNC4, "h");
    832 
    833         Uri rowUri = mResolver.insert(RawContacts.CONTENT_URI, values);
    834         long rawContactId = ContentUris.parseId(rowUri);
    835 
    836         assertStoredValues(rowUri, values);
    837         assertSelection(RawContacts.CONTENT_URI, values, RawContacts._ID, rawContactId);
    838         assertNetworkNotified(true);
    839     }
    840 
    841     public void testDataDirectoryWithLookupUri() {
    842         ContentValues values = new ContentValues();
    843 
    844         long rawContactId = RawContactUtil.createRawContactWithName(mResolver);
    845         insertPhoneNumber(rawContactId, "555-GOOG-411");
    846         insertEmail(rawContactId, "google (at) android.com");
    847 
    848         long contactId = queryContactId(rawContactId);
    849         String lookupKey = queryLookupKey(contactId);
    850 
    851         // Complete and valid lookup URI
    852         Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
    853         Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
    854 
    855         assertDataRows(dataUri, values);
    856 
    857         // Complete but stale lookup URI
    858         lookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
    859         dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
    860         assertDataRows(dataUri, values);
    861 
    862         // Incomplete lookup URI (lookup key only, no contact ID)
    863         dataUri = Uri.withAppendedPath(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
    864                 lookupKey), Contacts.Data.CONTENT_DIRECTORY);
    865         assertDataRows(dataUri, values);
    866     }
    867 
    868     private void assertDataRows(Uri dataUri, ContentValues values) {
    869         Cursor cursor = mResolver.query(dataUri, new String[]{ Data.DATA1 }, null, null, Data._ID);
    870         assertEquals(3, cursor.getCount());
    871         cursor.moveToFirst();
    872         values.put(Data.DATA1, "John Doe");
    873         assertCursorValues(cursor, values);
    874 
    875         cursor.moveToNext();
    876         values.put(Data.DATA1, "555-GOOG-411");
    877         assertCursorValues(cursor, values);
    878 
    879         cursor.moveToNext();
    880         values.put(Data.DATA1, "google (at) android.com");
    881         assertCursorValues(cursor, values);
    882 
    883         cursor.close();
    884     }
    885 
    886     public void testContactEntitiesWithIdBasedUri() {
    887         ContentValues values = new ContentValues();
    888         Account account1 = new Account("act1", "actype1");
    889         Account account2 = new Account("act2", "actype2");
    890 
    891         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, account1);
    892         insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
    893         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90,
    894                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
    895 
    896         long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2);
    897         setAggregationException(
    898                 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
    899 
    900         long contactId = queryContactId(rawContactId1);
    901 
    902         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
    903         Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
    904 
    905         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
    906     }
    907 
    908     public void testContactEntitiesWithLookupUri() {
    909         ContentValues values = new ContentValues();
    910         Account account1 = new Account("act1", "actype1");
    911         Account account2 = new Account("act2", "actype2");
    912 
    913         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, account1);
    914         insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk");
    915         insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90,
    916                 StatusUpdates.CAPABILITY_HAS_CAMERA, false);
    917 
    918         long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2);
    919         setAggregationException(
    920                 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2);
    921 
    922         long contactId = queryContactId(rawContactId1);
    923         String lookupKey = queryLookupKey(contactId);
    924 
    925         // First try with a matching contact ID
    926         Uri contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
    927         Uri entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
    928         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
    929 
    930         // Now try with a contact ID mismatch
    931         contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey);
    932         entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
    933         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
    934 
    935         // Now try without an ID altogether
    936         contactLookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
    937         entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY);
    938         assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2);
    939     }
    940 
    941     private void assertEntityRows(Uri entityUri, long contactId, long rawContactId1,
    942             long rawContactId2) {
    943         ContentValues values = new ContentValues();
    944 
    945         Cursor cursor = mResolver.query(entityUri, null, null, null,
    946                 Contacts.Entity.RAW_CONTACT_ID + "," + Contacts.Entity.DATA_ID);
    947         assertEquals(3, cursor.getCount());
    948 
    949         // First row - name
    950         cursor.moveToFirst();
    951         values.put(Contacts.Entity.CONTACT_ID, contactId);
    952         values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1);
    953         values.put(Contacts.Entity.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
    954         values.put(Contacts.Entity.DATA1, "John Doe");
    955         values.put(Contacts.Entity.ACCOUNT_NAME, "act1");
    956         values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1");
    957         values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
    958         values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
    959         values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
    960         values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
    961         values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
    962         values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
    963         values.putNull(Contacts.Entity.PRESENCE);
    964         assertCursorValues(cursor, values);
    965 
    966         // Second row - IM
    967         cursor.moveToNext();
    968         values.put(Contacts.Entity.CONTACT_ID, contactId);
    969         values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1);
    970         values.put(Contacts.Entity.MIMETYPE, Im.CONTENT_ITEM_TYPE);
    971         values.put(Contacts.Entity.DATA1, "gtalk");
    972         values.put(Contacts.Entity.ACCOUNT_NAME, "act1");
    973         values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1");
    974         values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
    975         values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
    976         values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
    977         values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
    978         values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
    979         values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
    980         values.put(Contacts.Entity.PRESENCE, StatusUpdates.IDLE);
    981         assertCursorValues(cursor, values);
    982 
    983         // Third row - second raw contact, not data
    984         cursor.moveToNext();
    985         values.put(Contacts.Entity.CONTACT_ID, contactId);
    986         values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId2);
    987         values.putNull(Contacts.Entity.MIMETYPE);
    988         values.putNull(Contacts.Entity.DATA_ID);
    989         values.putNull(Contacts.Entity.DATA1);
    990         values.put(Contacts.Entity.ACCOUNT_NAME, "act2");
    991         values.put(Contacts.Entity.ACCOUNT_TYPE, "actype2");
    992         values.put(Contacts.Entity.DISPLAY_NAME, "John Doe");
    993         values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John");
    994         values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1);
    995         values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
    996         values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE);
    997         values.put(Contacts.Entity.CONTACT_STATUS, "Busy");
    998         values.putNull(Contacts.Entity.PRESENCE);
    999         assertCursorValues(cursor, values);
   1000 
   1001         cursor.close();
   1002     }
   1003 
   1004     public void testDataInsert() {
   1005         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe");
   1006 
   1007         ContentValues values = new ContentValues();
   1008         putDataValues(values, rawContactId);
   1009         Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
   1010         long dataId = ContentUris.parseId(dataUri);
   1011 
   1012         long contactId = queryContactId(rawContactId);
   1013         values.put(RawContacts.CONTACT_ID, contactId);
   1014         assertStoredValues(dataUri, values);
   1015 
   1016         assertSelection(Data.CONTENT_URI, values, Data._ID, dataId);
   1017 
   1018         // Access the same data through the directory under RawContacts
   1019         Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
   1020         Uri rawContactDataUri =
   1021                 Uri.withAppendedPath(rawContactUri, RawContacts.Data.CONTENT_DIRECTORY);
   1022         assertSelection(rawContactDataUri, values, Data._ID, dataId);
   1023 
   1024         // Access the same data through the directory under Contacts
   1025         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   1026         Uri contactDataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY);
   1027         assertSelection(contactDataUri, values, Data._ID, dataId);
   1028         assertNetworkNotified(true);
   1029     }
   1030 
   1031     public void testDataInsertPhoneNumberTooLongIsTrimmed() {
   1032         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe");
   1033 
   1034         ContentValues values = new ContentValues();
   1035         values.put(Data.RAW_CONTACT_ID, rawContactId);
   1036         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1037         final StringBuilder sb = new StringBuilder();
   1038         for (int i = 0; i < 300; i++) {
   1039             sb.append("12345");
   1040         }
   1041         final String phoneNumber1500Chars = sb.toString();
   1042         values.put(Phone.NUMBER, phoneNumber1500Chars);
   1043 
   1044         Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
   1045         final long dataId = ContentUris.parseId(dataUri);
   1046 
   1047         sb.setLength(0);
   1048         for (int i = 0; i < 200; i++) {
   1049             sb.append("12345");
   1050         }
   1051         final String phoneNumber1000Chars = sb.toString();
   1052         final ContentValues expected = new ContentValues();
   1053         expected.put(Phone.NUMBER, phoneNumber1000Chars);
   1054         assertSelection(dataUri, expected, Data._ID, dataId);
   1055     }
   1056 
   1057     public void testRawContactDataQuery() {
   1058         Account account1 = new Account("a", "b");
   1059         Account account2 = new Account("c", "d");
   1060         long rawContactId1 = RawContactUtil.createRawContact(mResolver, account1);
   1061         Uri dataUri1 = DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Doe");
   1062         long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2);
   1063         Uri dataUri2 = DataUtil.insertStructuredName(mResolver, rawContactId2, "Jane", "Doe");
   1064 
   1065         Uri uri1 = TestUtil.maybeAddAccountQueryParameters(dataUri1, account1);
   1066         Uri uri2 = TestUtil.maybeAddAccountQueryParameters(dataUri2, account2);
   1067         assertStoredValue(uri1, Data._ID, ContentUris.parseId(dataUri1)) ;
   1068         assertStoredValue(uri2, Data._ID, ContentUris.parseId(dataUri2)) ;
   1069     }
   1070 
   1071     public void testPhonesQuery() {
   1072 
   1073         ContentValues values = new ContentValues();
   1074         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1075         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1076         values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
   1077         values.put(RawContacts.TIMES_CONTACTED, 54321);
   1078         values.put(RawContacts.STARRED, 1);
   1079 
   1080         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1081         long rawContactId = ContentUris.parseId(rawContactUri);
   1082 
   1083         DataUtil.insertStructuredName(mResolver, rawContactId, "Meghan", "Knox");
   1084         Uri uri = insertPhoneNumber(rawContactId, "18004664411");
   1085         long phoneId = ContentUris.parseId(uri);
   1086 
   1087 
   1088         long contactId = queryContactId(rawContactId);
   1089         values.clear();
   1090         values.put(Data._ID, phoneId);
   1091         values.put(Data.RAW_CONTACT_ID, rawContactId);
   1092         values.put(RawContacts.CONTACT_ID, contactId);
   1093         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1094         values.put(Phone.NUMBER, "18004664411");
   1095         values.put(Phone.TYPE, Phone.TYPE_HOME);
   1096         values.putNull(Phone.LABEL);
   1097         values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
   1098         values.put(Contacts.CUSTOM_RINGTONE, "d");
   1099         values.put(Contacts.SEND_TO_VOICEMAIL, 1);
   1100         values.put(Contacts.LAST_TIME_CONTACTED, 12345);
   1101         values.put(Contacts.TIMES_CONTACTED, 54321);
   1102         values.put(Contacts.STARRED, 1);
   1103 
   1104         assertStoredValues(ContentUris.withAppendedId(Phone.CONTENT_URI, phoneId), values);
   1105         assertSelection(Phone.CONTENT_URI, values, Data._ID, phoneId);
   1106     }
   1107 
   1108     public void testPhonesWithMergedContacts() {
   1109         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
   1110         insertPhoneNumber(rawContactId1, "123456789", true);
   1111 
   1112         long rawContactId2 = RawContactUtil.createRawContact(mResolver);
   1113         insertPhoneNumber(rawContactId2, "123456789", true);
   1114 
   1115         setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE,
   1116                 rawContactId1, rawContactId2);
   1117         assertNotAggregated(rawContactId1, rawContactId2);
   1118 
   1119         ContentValues values1 = new ContentValues();
   1120         values1.put(Contacts.DISPLAY_NAME, "123456789");
   1121         values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1122         values1.put(Phone.NUMBER, "123456789");
   1123 
   1124         // There are two phone numbers, so we should get two rows.
   1125         assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1});
   1126 
   1127         // Now set the dedupe flag.  But still we should get two rows, because they're two
   1128         // different contacts.  We only dedupe within each contact.
   1129         final Uri dedupeUri = Phone.CONTENT_URI.buildUpon()
   1130                 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
   1131                 .build();
   1132         assertStoredValues(dedupeUri, new ContentValues[] {values1, values1});
   1133 
   1134         // Now join them into a single contact.
   1135         setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER,
   1136                 rawContactId1, rawContactId2);
   1137 
   1138         assertAggregated(rawContactId1, rawContactId2, "123456789");
   1139 
   1140         // Contact merge won't affect the default result of Phone Uri, where we don't dedupe.
   1141         assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1});
   1142 
   1143         // Now we dedupe them.
   1144         assertStoredValues(dedupeUri, values1);
   1145     }
   1146 
   1147     public void testPhonesNormalizedNumber() {
   1148         final long rawContactId = RawContactUtil.createRawContact(mResolver);
   1149 
   1150         // Write both a number and a normalized number. Those should be written as-is
   1151         final ContentValues values = new ContentValues();
   1152         values.put(Data.RAW_CONTACT_ID, rawContactId);
   1153         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1154         values.put(Phone.NUMBER, "1234");
   1155         values.put(Phone.NORMALIZED_NUMBER, "5678");
   1156         values.put(Phone.TYPE, Phone.TYPE_HOME);
   1157 
   1158         final Uri dataUri = mResolver.insert(Data.CONTENT_URI, values);
   1159 
   1160         // Check the lookup table.
   1161         assertEquals(1,
   1162                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1234"), null, null));
   1163         assertEquals(1,
   1164                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "5678"), null, null));
   1165 
   1166         // Check the data table.
   1167         assertStoredValues(dataUri,
   1168                 cv(Phone.NUMBER, "1234", Phone.NORMALIZED_NUMBER, "5678")
   1169                 );
   1170 
   1171         // Replace both in an UPDATE
   1172         values.clear();
   1173         values.put(Phone.NUMBER, "4321");
   1174         values.put(Phone.NORMALIZED_NUMBER, "8765");
   1175         mResolver.update(dataUri, values, null, null);
   1176         assertEquals(0,
   1177                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1234"), null, null));
   1178         assertEquals(1,
   1179                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "4321"), null, null));
   1180         assertEquals(0,
   1181                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "5678"), null, null));
   1182         assertEquals(1,
   1183                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null));
   1184 
   1185         assertStoredValues(dataUri,
   1186                 cv(Phone.NUMBER, "4321", Phone.NORMALIZED_NUMBER, "8765")
   1187                 );
   1188 
   1189         // Replace only NUMBER ==> NORMALIZED_NUMBER will be inferred (we test that by making
   1190         // sure the old manual value can not be found anymore)
   1191         values.clear();
   1192         values.put(Phone.NUMBER, "+1-800-466-5432");
   1193         mResolver.update(dataUri, values, null, null);
   1194         assertEquals(
   1195                 1,
   1196                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "+1-800-466-5432"), null,
   1197                         null));
   1198         assertEquals(0,
   1199                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null));
   1200 
   1201         assertStoredValues(dataUri,
   1202                 cv(Phone.NUMBER, "+1-800-466-5432", Phone.NORMALIZED_NUMBER, "+18004665432")
   1203                 );
   1204 
   1205         // Replace only NORMALIZED_NUMBER ==> call is ignored, things will be unchanged
   1206         values.clear();
   1207         values.put(Phone.NORMALIZED_NUMBER, "8765");
   1208         mResolver.update(dataUri, values, null, null);
   1209         assertEquals(
   1210                 1,
   1211                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "+1-800-466-5432"), null,
   1212                         null));
   1213         assertEquals(0,
   1214                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null));
   1215 
   1216         assertStoredValues(dataUri,
   1217                 cv(Phone.NUMBER, "+1-800-466-5432", Phone.NORMALIZED_NUMBER, "+18004665432")
   1218                 );
   1219 
   1220         // Replace NUMBER with an "invalid" number which can't be normalized.  It should clear
   1221         // NORMALIZED_NUMBER.
   1222 
   1223         // 1. Set 999 to NORMALIZED_NUMBER explicitly.
   1224         values.clear();
   1225         values.put(Phone.NUMBER, "888");
   1226         values.put(Phone.NORMALIZED_NUMBER, "999");
   1227         mResolver.update(dataUri, values, null, null);
   1228 
   1229         assertEquals(1,
   1230                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "999"), null, null));
   1231 
   1232         assertStoredValues(dataUri,
   1233                 cv(Phone.NUMBER, "888", Phone.NORMALIZED_NUMBER, "999")
   1234                 );
   1235 
   1236         // 2. Set an invalid number to NUMBER.
   1237         values.clear();
   1238         values.put(Phone.NUMBER, "1");
   1239         mResolver.update(dataUri, values, null, null);
   1240 
   1241         assertEquals(0,
   1242                 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "999"), null, null));
   1243 
   1244         assertStoredValues(dataUri,
   1245                 cv(Phone.NUMBER, "1", Phone.NORMALIZED_NUMBER, null)
   1246                 );
   1247     }
   1248 
   1249     public void testPhonesFilterQuery() {
   1250         testPhonesFilterQueryInter(Phone.CONTENT_FILTER_URI);
   1251     }
   1252 
   1253     /**
   1254      * A convenient method for {@link #testPhonesFilterQuery()} and
   1255      * {@link #testCallablesFilterQuery()}.
   1256      *
   1257      * This confirms if both URIs return identical results for phone-only contacts and
   1258      * appropriately different results for contacts with sip addresses.
   1259      *
   1260      * @param baseFilterUri Either {@link Phone#CONTENT_FILTER_URI} or
   1261      * {@link Callable#CONTENT_FILTER_URI}.
   1262      */
   1263     private void testPhonesFilterQueryInter(Uri baseFilterUri) {
   1264         assertTrue("Unsupported Uri (" + baseFilterUri + ")",
   1265                 Phone.CONTENT_FILTER_URI.equals(baseFilterUri)
   1266                         || Callable.CONTENT_FILTER_URI.equals(baseFilterUri));
   1267 
   1268         final long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Hot",
   1269                 "Tamale", TestUtil.ACCOUNT_1);
   1270         insertPhoneNumber(rawContactId1, "1-800-466-4411");
   1271 
   1272         final long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Chilled",
   1273                 "Guacamole", TestUtil.ACCOUNT_2);
   1274         insertPhoneNumber(rawContactId2, "1-800-466-5432");
   1275         insertPhoneNumber(rawContactId2, "0 (at) example.com", false, Phone.TYPE_PAGER);
   1276         insertPhoneNumber(rawContactId2, "1 (at) example.com", false, Phone.TYPE_PAGER);
   1277 
   1278         final Uri filterUri1 = Uri.withAppendedPath(baseFilterUri, "tamale");
   1279         ContentValues values = new ContentValues();
   1280         values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   1281         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1282         values.put(Phone.NUMBER, "1-800-466-4411");
   1283         values.put(Phone.TYPE, Phone.TYPE_HOME);
   1284         values.putNull(Phone.LABEL);
   1285         assertStoredValuesWithProjection(filterUri1, values);
   1286 
   1287         final Uri filterUri2 = Uri.withAppendedPath(baseFilterUri, "1-800-GOOG-411");
   1288         assertStoredValues(filterUri2, values);
   1289 
   1290         final Uri filterUri3 = Uri.withAppendedPath(baseFilterUri, "18004664");
   1291         assertStoredValues(filterUri3, values);
   1292 
   1293         final Uri filterUri4 = Uri.withAppendedPath(baseFilterUri, "encilada");
   1294         assertEquals(0, getCount(filterUri4, null, null));
   1295 
   1296         final Uri filterUri5 = Uri.withAppendedPath(baseFilterUri, "*");
   1297         assertEquals(0, getCount(filterUri5, null, null));
   1298 
   1299         ContentValues values1 = new ContentValues();
   1300         values1.put(Contacts.DISPLAY_NAME, "Chilled Guacamole");
   1301         values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1302         values1.put(Phone.NUMBER, "1-800-466-5432");
   1303         values1.put(Phone.TYPE, Phone.TYPE_HOME);
   1304         values1.putNull(Phone.LABEL);
   1305 
   1306         ContentValues values2 = new ContentValues();
   1307         values2.put(Contacts.DISPLAY_NAME, "Chilled Guacamole");
   1308         values2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1309         values2.put(Phone.NUMBER, "0 (at) example.com");
   1310         values2.put(Phone.TYPE, Phone.TYPE_PAGER);
   1311         values2.putNull(Phone.LABEL);
   1312 
   1313         ContentValues values3 = new ContentValues();
   1314         values3.put(Contacts.DISPLAY_NAME, "Chilled Guacamole");
   1315         values3.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1316         values3.put(Phone.NUMBER, "1 (at) example.com");
   1317         values3.put(Phone.TYPE, Phone.TYPE_PAGER);
   1318         values3.putNull(Phone.LABEL);
   1319 
   1320         final Uri filterUri6 = Uri.withAppendedPath(baseFilterUri, "Chilled");
   1321         assertStoredValues(filterUri6, new ContentValues[]{values1, values2, values3});
   1322 
   1323         // Insert a SIP address. From here, Phone URI and Callable URI may return different results
   1324         // than each other.
   1325         insertSipAddress(rawContactId1, "sip_hot_tamale (at) example.com");
   1326         insertSipAddress(rawContactId1, "sip:sip_hot (at) example.com");
   1327 
   1328         final Uri filterUri7 = Uri.withAppendedPath(baseFilterUri, "sip_hot");
   1329         final Uri filterUri8 = Uri.withAppendedPath(baseFilterUri, "sip_hot_tamale");
   1330         if (Callable.CONTENT_FILTER_URI.equals(baseFilterUri)) {
   1331             ContentValues values4 = new ContentValues();
   1332             values4.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   1333             values4.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
   1334             values4.put(SipAddress.SIP_ADDRESS, "sip_hot_tamale (at) example.com");
   1335 
   1336             ContentValues values5 = new ContentValues();
   1337             values5.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   1338             values5.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
   1339             values5.put(SipAddress.SIP_ADDRESS, "sip:sip_hot (at) example.com");
   1340             assertStoredValues(filterUri1, new ContentValues[] {values, values4, values5});
   1341 
   1342             assertStoredValues(filterUri7, new ContentValues[] {values4, values5});
   1343             assertStoredValues(filterUri8, values4);
   1344         } else {
   1345             // Sip address should not affect Phone URI.
   1346             assertStoredValuesWithProjection(filterUri1, values);
   1347             assertEquals(0, getCount(filterUri7, null, null));
   1348         }
   1349 
   1350         // Sanity test. Run tests for "Chilled Guacamole" again and see nothing changes
   1351         // after the Sip address being inserted.
   1352         assertStoredValues(filterUri2, values);
   1353         assertEquals(0, getCount(filterUri4, null, null));
   1354         assertEquals(0, getCount(filterUri5, null, null));
   1355         assertStoredValues(filterUri6, new ContentValues[] {values1, values2, values3} );
   1356     }
   1357 
   1358     public void testPhonesFilterSearchParams() {
   1359         final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "Dad", null);
   1360         insertPhoneNumber(rid1, "123-456-7890");
   1361 
   1362         final long rid2 = RawContactUtil.createRawContactWithName(mResolver, "Mam", null);
   1363         insertPhoneNumber(rid2, "323-123-4567");
   1364 
   1365         // By default, "dad" will match both the display name and the phone number.
   1366         // Because "dad" is "323" after the dialpad conversion, it'll match "Mam" too.
   1367         assertStoredValues(
   1368                 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad").build(),
   1369                 cv(Phone.DISPLAY_NAME, "Dad", Phone.NUMBER, "123-456-7890"),
   1370                 cv(Phone.DISPLAY_NAME, "Mam", Phone.NUMBER, "323-123-4567")
   1371                 );
   1372         assertStoredValues(
   1373                 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad")
   1374                     .appendQueryParameter(Phone.SEARCH_PHONE_NUMBER_KEY, "0")
   1375                     .build(),
   1376                 cv(Phone.DISPLAY_NAME, "Dad", Phone.NUMBER, "123-456-7890")
   1377                 );
   1378 
   1379         assertStoredValues(
   1380                 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad")
   1381                     .appendQueryParameter(Phone.SEARCH_DISPLAY_NAME_KEY, "0")
   1382                     .build(),
   1383                 cv(Phone.DISPLAY_NAME, "Mam", Phone.NUMBER, "323-123-4567")
   1384                 );
   1385         assertStoredValues(
   1386                 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad")
   1387                         .appendQueryParameter(Phone.SEARCH_DISPLAY_NAME_KEY, "0")
   1388                         .appendQueryParameter(Phone.SEARCH_PHONE_NUMBER_KEY, "0")
   1389                         .build()
   1390         );
   1391     }
   1392 
   1393     public void testPhoneLookup() {
   1394         ContentValues values = new ContentValues();
   1395         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1396         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1397 
   1398         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1399         long rawContactId = ContentUris.parseId(rawContactUri);
   1400 
   1401         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale");
   1402         insertPhoneNumber(rawContactId, "18004664411");
   1403 
   1404         // We'll create two lookup records, 18004664411 and +18004664411, and the below lookup
   1405         // will match both.
   1406 
   1407         Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
   1408 
   1409         values.clear();
   1410         values.put(PhoneLookup._ID, queryContactId(rawContactId));
   1411         values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale");
   1412         values.put(PhoneLookup.NUMBER, "18004664411");
   1413         values.put(PhoneLookup.TYPE, Phone.TYPE_HOME);
   1414         values.putNull(PhoneLookup.LABEL);
   1415         values.put(PhoneLookup.CUSTOM_RINGTONE, "d");
   1416         values.put(PhoneLookup.SEND_TO_VOICEMAIL, 1);
   1417         assertStoredValues(lookupUri1, null, null, new ContentValues[] {values, values});
   1418 
   1419         // In the context that 8004664411 is a valid number, "4664411" as a
   1420         // call id should  match to both "8004664411" and "+18004664411".
   1421         Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411");
   1422         assertEquals(2, getCount(lookupUri2, null, null));
   1423 
   1424         // A wrong area code 799 vs 800 should not be matched
   1425         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "7994664411");
   1426         assertEquals(0, getCount(lookupUri2, null, null));
   1427     }
   1428 
   1429     public void testPhoneLookupStarUseCases() {
   1430         // Create two raw contacts with numbers "*123" and "12 3". This is a real life example
   1431         // from b/13195334.
   1432         final ContentValues values = new ContentValues();
   1433         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1434         long rawContactId = ContentUris.parseId(rawContactUri);
   1435         DataUtil.insertStructuredName(mResolver, rawContactId, "Emergency", /* familyName =*/ null);
   1436         insertPhoneNumber(rawContactId, "*123");
   1437 
   1438         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1439         rawContactId = ContentUris.parseId(rawContactUri);
   1440         DataUtil.insertStructuredName(mResolver, rawContactId, "Voicemail", /* familyName =*/ null);
   1441         insertPhoneNumber(rawContactId, "12 3");
   1442 
   1443         // Verify: "123" returns the "Voicemail" raw contact id. It should not match
   1444         // a phone number that starts with a "*".
   1445         Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "123");
   1446         values.clear();
   1447         values.put(PhoneLookup.DISPLAY_NAME, "Voicemail");
   1448         assertStoredValues(lookupUri, null, null, new ContentValues[] {values});
   1449 
   1450         lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "(1) 23");
   1451         values.clear();
   1452         values.put(PhoneLookup.DISPLAY_NAME, "Voicemail");
   1453         assertStoredValues(lookupUri, null, null, new ContentValues[] {values});
   1454 
   1455         // Verify: "*123" returns the "Emergency" raw contact id.
   1456         lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*1-23");
   1457         values.clear();
   1458         values.put(PhoneLookup.DISPLAY_NAME, "Emergency");
   1459         assertStoredValues(lookupUri, null, null, new ContentValues[] {values});
   1460     }
   1461 
   1462     public void testPhoneLookupReturnsNothingRatherThanStar() {
   1463         // Create Emergency raw contact with "*123456789" number.
   1464         final ContentValues values = new ContentValues();
   1465         final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1466         final long rawContactId1 = ContentUris.parseId(rawContactUri);
   1467         DataUtil.insertStructuredName(mResolver, rawContactId1, "Emergency",
   1468                 /* familyName =*/ null);
   1469         insertPhoneNumber(rawContactId1, "*123456789");
   1470 
   1471         // Lookup should return no results. It does not ignore stars even when no other matches.
   1472         final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "123456789");
   1473         assertEquals(0, getCount(lookupUri, null, null));
   1474     }
   1475 
   1476     public void testPhoneLookupReturnsNothingRatherThanMissStar() {
   1477         // Create Voice Mail raw contact with "123456789" number.
   1478         final ContentValues values = new ContentValues();
   1479         final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1480         final long rawContactId1 = ContentUris.parseId(rawContactUri);
   1481         DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail",
   1482                 /* familyName =*/ null);
   1483         insertPhoneNumber(rawContactId1, "123456789");
   1484 
   1485         // Lookup should return no results. It does not ignore stars even when no other matches.
   1486         final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*123456789");
   1487         assertEquals(0, getCount(lookupUri, null, null));
   1488     }
   1489 
   1490     public void testPhoneLookupStarNoFallbackMatch() {
   1491         final ContentValues values = new ContentValues();
   1492         final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1493         final long rawContactId1 = ContentUris.parseId(rawContactUri);
   1494         DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail",
   1495                 /* familyName =*/ null);
   1496         insertPhoneNumber(rawContactId1, "*011123456789");
   1497 
   1498         // The numbers "+123456789" and "*011123456789" are a "fallback" match. The + is equivalent
   1499         // to "011". This lookup should return no results. Lookup does not ignore
   1500         // stars, even when doing a fallback lookup.
   1501         final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+123456789");
   1502         assertEquals(0, getCount(lookupUri, null, null));
   1503     }
   1504 
   1505     public void testPhoneLookupStarNotBreakFallbackMatching() {
   1506         // Create a raw contact with a phone number starting with "011"
   1507         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
   1508         long rawContactId = ContentUris.parseId(rawContactUri);
   1509         DataUtil.insertStructuredName(mResolver, rawContactId, "No star",
   1510                 /* familyName =*/ null);
   1511         insertPhoneNumber(rawContactId, "011123456789");
   1512 
   1513         // Create a raw contact with a phone number starting with "*011"
   1514         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues());
   1515         rawContactId = ContentUris.parseId(rawContactUri);
   1516         DataUtil.insertStructuredName(mResolver, rawContactId, "Has star",
   1517                 /* familyName =*/ null);
   1518         insertPhoneNumber(rawContactId, "*011123456789");
   1519 
   1520         // A phone number starting with "+" can (fallback) match the same phone number starting
   1521         // with "001". Verify that this fallback matching still occurs in the presence of
   1522         // numbers starting with "*"s.
   1523         final Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
   1524                 "+123456789");
   1525         final ContentValues values = new ContentValues();
   1526         values.put(PhoneLookup.DISPLAY_NAME, "No star");
   1527         assertStoredValues(lookupUri1, null, null, new ContentValues[]{values});
   1528     }
   1529 
   1530     public void testPhoneLookupExplicitProjection() {
   1531         final ContentValues values = new ContentValues();
   1532         final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1533         final long rawContactId1 = ContentUris.parseId(rawContactUri);
   1534         DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail",
   1535                 /* familyName =*/ null);
   1536         insertPhoneNumber(rawContactId1, "+1234567");
   1537 
   1538         // Performing a query with a non-null projection with or without PhoneLookup.Number inside
   1539         // it should not cause a crash.
   1540         Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "1234567");
   1541         String[] projection = new String[] {PhoneLookup.DISPLAY_NAME};
   1542         mResolver.query(lookupUri, projection, null, null, null);
   1543         projection = new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.NUMBER};
   1544         mResolver.query(lookupUri, projection, null, null, null);
   1545 
   1546         // Shouldn't crash for a fallback query either
   1547         lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*0111234567");
   1548         projection = new String[] {PhoneLookup.DISPLAY_NAME};
   1549         mResolver.query(lookupUri, projection, null, null, null);
   1550         projection = new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.NUMBER};
   1551         mResolver.query(lookupUri, projection, null, null, null);
   1552     }
   1553 
   1554     public void testPhoneLookupUseCases() {
   1555         ContentValues values = new ContentValues();
   1556         Uri rawContactUri;
   1557         long rawContactId;
   1558         Uri lookupUri2;
   1559 
   1560         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1561         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1562 
   1563         // International format in contacts
   1564         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1565         rawContactId = ContentUris.parseId(rawContactUri);
   1566 
   1567         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale");
   1568         insertPhoneNumber(rawContactId, "+1-650-861-0000");
   1569 
   1570         values.clear();
   1571 
   1572         // match with international format
   1573         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0000");
   1574         assertEquals(1, getCount(lookupUri2, null, null));
   1575 
   1576         // match with national format
   1577         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0000");
   1578         assertEquals(1, getCount(lookupUri2, null, null));
   1579 
   1580         // does not match with wrong area code
   1581         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "649 861 0000");
   1582         assertEquals(0, getCount(lookupUri2, null, null));
   1583 
   1584         // does not match with missing digits in mistyped area code
   1585         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "5 861 0000");
   1586         assertEquals(0, getCount(lookupUri2, null, null));
   1587 
   1588         // does not match with missing digit in mistyped area code
   1589         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "65 861 0000");
   1590         assertEquals(0, getCount(lookupUri2, null, null));
   1591 
   1592         // National format in contacts
   1593         values.clear();
   1594         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1595         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1596         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1597         rawContactId = ContentUris.parseId(rawContactUri);
   1598 
   1599         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot1", "Tamale");
   1600         insertPhoneNumber(rawContactId, "650-861-0001");
   1601 
   1602         values.clear();
   1603 
   1604         // match with international format
   1605         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0001");
   1606         assertEquals(2, getCount(lookupUri2, null, null));
   1607 
   1608         // match with national format
   1609         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0001");
   1610         assertEquals(2, getCount(lookupUri2, null, null));
   1611 
   1612         // Local format in contacts
   1613         values.clear();
   1614         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1615         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1616         rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   1617         rawContactId = ContentUris.parseId(rawContactUri);
   1618 
   1619         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot2", "Tamale");
   1620         insertPhoneNumber(rawContactId, "861-0002");
   1621 
   1622         values.clear();
   1623 
   1624         // match with international format
   1625         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0002");
   1626         assertEquals(1, getCount(lookupUri2, null, null));
   1627 
   1628         // match with national format
   1629         lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0002");
   1630         assertEquals(1, getCount(lookupUri2, null, null));
   1631     }
   1632 
   1633     public void testIntlPhoneLookupUseCases() {
   1634         // Checks the logic that relies on phone_number_compare_loose(Gingerbread) as a fallback
   1635         //for phone number lookups.
   1636         String fullNumber = "01197297427289";
   1637 
   1638         ContentValues values = new ContentValues();
   1639         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1640         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1641         long rawContactId = ContentUris.parseId(mResolver.insert(RawContacts.CONTENT_URI, values));
   1642         DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang");
   1643         insertPhoneNumber(rawContactId, fullNumber);
   1644 
   1645         // Full number should definitely match.
   1646         assertEquals(2, getCount(Uri.withAppendedPath(
   1647                 PhoneLookup.CONTENT_FILTER_URI, fullNumber), null, null));
   1648 
   1649         // Shorter (local) number with 0 prefix should also match.
   1650         assertEquals(2, getCount(Uri.withAppendedPath(
   1651                 PhoneLookup.CONTENT_FILTER_URI, "097427289"), null, null));
   1652 
   1653         // Number with international (+972) prefix should also match.
   1654         assertEquals(1, getCount(Uri.withAppendedPath(
   1655                 PhoneLookup.CONTENT_FILTER_URI, "+97297427289"), null, null));
   1656 
   1657         // Same shorter number with dashes should match.
   1658         assertEquals(2, getCount(Uri.withAppendedPath(
   1659                 PhoneLookup.CONTENT_FILTER_URI, "09-742-7289"), null, null));
   1660 
   1661         // Same shorter number with spaces should match.
   1662         assertEquals(2, getCount(Uri.withAppendedPath(
   1663                 PhoneLookup.CONTENT_FILTER_URI, "09 742 7289"), null, null));
   1664 
   1665         // Some other number should not match.
   1666         assertEquals(0, getCount(Uri.withAppendedPath(
   1667                 PhoneLookup.CONTENT_FILTER_URI, "049102395"), null, null));
   1668     }
   1669 
   1670     public void testPhoneLookupB5252190() {
   1671         // Test cases from b/5252190
   1672         String storedNumber = "796010101";
   1673 
   1674         ContentValues values = new ContentValues();
   1675         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1676         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1677         long rawContactId = ContentUris.parseId(mResolver.insert(RawContacts.CONTENT_URI, values));
   1678         DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang");
   1679         insertPhoneNumber(rawContactId, storedNumber);
   1680 
   1681         assertEquals(1, getCount(Uri.withAppendedPath(
   1682                 PhoneLookup.CONTENT_FILTER_URI, "0796010101"), null, null));
   1683 
   1684         assertEquals(1, getCount(Uri.withAppendedPath(
   1685                 PhoneLookup.CONTENT_FILTER_URI, "+48796010101"), null, null));
   1686 
   1687         assertEquals(1, getCount(Uri.withAppendedPath(
   1688                 PhoneLookup.CONTENT_FILTER_URI, "48796010101"), null, null));
   1689 
   1690         assertEquals(1, getCount(Uri.withAppendedPath(
   1691                 PhoneLookup.CONTENT_FILTER_URI, "4-879-601-0101"), null, null));
   1692 
   1693         assertEquals(1, getCount(Uri.withAppendedPath(
   1694                 PhoneLookup.CONTENT_FILTER_URI, "4 879 601 0101"), null, null));
   1695     }
   1696 
   1697     public void testPhoneLookupUseStrictPhoneNumberCompare() {
   1698         // Test lookup cases when mUseStrictPhoneNumberComparison is true
   1699         final ContactsProvider2 cp = (ContactsProvider2) getProvider();
   1700         final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest();
   1701         // Get and save the original value of mUseStrictPhoneNumberComparison so that we
   1702         // can restore it when we are done with the test
   1703         final boolean oldUseStrict = dbHelper.getUseStrictPhoneNumberComparisonForTest();
   1704         dbHelper.setUseStrictPhoneNumberComparisonForTest(true);
   1705 
   1706 
   1707         try {
   1708             String fullNumber = "01197297427289";
   1709             ContentValues values = new ContentValues();
   1710             values.put(RawContacts.CUSTOM_RINGTONE, "d");
   1711             values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   1712             long rawContactId = ContentUris.parseId(
   1713                     mResolver.insert(RawContacts.CONTENT_URI, values));
   1714             DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang");
   1715             insertPhoneNumber(rawContactId, fullNumber);
   1716             insertPhoneNumber(rawContactId, "5103337596");
   1717             insertPhoneNumber(rawContactId, "+19012345678");
   1718             // One match for full number
   1719             assertEquals(1, getCount(Uri.withAppendedPath(
   1720                     PhoneLookup.CONTENT_FILTER_URI, fullNumber), null, null));
   1721 
   1722             // No matches for extra digit at the front
   1723             assertEquals(0, getCount(Uri.withAppendedPath(
   1724                     PhoneLookup.CONTENT_FILTER_URI, "55103337596"), null, null));
   1725             // No matches for mispelled area code
   1726             assertEquals(0, getCount(Uri.withAppendedPath(
   1727                     PhoneLookup.CONTENT_FILTER_URI, "5123337596"), null, null));
   1728 
   1729             // One match for matching number with dashes
   1730             assertEquals(1, getCount(Uri.withAppendedPath(
   1731                     PhoneLookup.CONTENT_FILTER_URI, "510-333-7596"), null, null));
   1732 
   1733             // One match for matching number with international code
   1734             assertEquals(1, getCount(Uri.withAppendedPath(
   1735                     PhoneLookup.CONTENT_FILTER_URI, "+1-510-333-7596"), null, null));
   1736             values.clear();
   1737 
   1738             // No matches for extra 0 in front
   1739             assertEquals(0, getCount(Uri.withAppendedPath(
   1740                     PhoneLookup.CONTENT_FILTER_URI, "0-510-333-7596"), null, null));
   1741             values.clear();
   1742 
   1743             // No matches for different country code
   1744             assertEquals(0, getCount(Uri.withAppendedPath(
   1745                     PhoneLookup.CONTENT_FILTER_URI, "+819012345678"), null, null));
   1746             values.clear();
   1747         } finally {
   1748             // restore the original value of mUseStrictPhoneNumberComparison
   1749             // upon test completion or failure
   1750             dbHelper.setUseStrictPhoneNumberComparisonForTest(oldUseStrict);
   1751         }
   1752     }
   1753 
   1754     /**
   1755      * Test for enterprise caller-id, but with no corp profile.
   1756      */
   1757     public void testPhoneLookupEnterprise_noCorpProfile() throws Exception {
   1758 
   1759         Uri uri1 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-111-1111");
   1760 
   1761         // No contacts profile, no data.
   1762         assertEquals(0, getCount(uri1));
   1763 
   1764         // Insert a contact into the primary CP2.
   1765         long rawContactId = ContentUris.parseId(
   1766                 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()));
   1767         DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe");
   1768         insertPhoneNumber(rawContactId, "408-111-1111");
   1769 
   1770         // Do the query again and check the result.
   1771         Cursor c = mResolver.query(uri1, null, null, null, null);
   1772         try {
   1773             assertEquals(1, c.getCount());
   1774             c.moveToPosition(0);
   1775             long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID));
   1776             assertFalse(Contacts.isEnterpriseContactId(contactId)); // Make sure it's not rewritten.
   1777         } finally {
   1778             c.close();
   1779         }
   1780     }
   1781 
   1782     /**
   1783      * Set up the corp user / CP2 and returns the corp CP2 instance.
   1784      *
   1785      * Create a second instance of CP2, and add it to the resolver, with the "user-id@" authority.
   1786      */
   1787     private SynchronousContactsProvider2 setUpCorpProvider() throws Exception {
   1788         mActor.mockUserManager.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.CORP_USER);
   1789 
   1790         // Note here we use a standalone CP2 so it'll have its own db helper.
   1791         // Also use AlteringUserContext here to report the corp user id.
   1792         return mActor.addProvider(StandaloneContactsProvider2.class,
   1793                 "" + MockUserManager.CORP_USER.id + "@com.android.contacts",
   1794                 new AlteringUserContext(mActor.getProviderContext(), MockUserManager.CORP_USER.id));
   1795     }
   1796 
   1797     /**
   1798      * Test for enterprise caller-id, with the corp profile.
   1799      *
   1800      * Note: in this test, we add one more provider instance for the authority
   1801      * "10 (at) com.android.contacts" and use it as the corp cp2.
   1802      */
   1803     public void testPhoneLookupEnterprise_withCorpProfile() throws Exception {
   1804         final SynchronousContactsProvider2 corpCp2 = setUpCorpProvider();
   1805 
   1806         Uri uri1 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-111-1111");
   1807         Uri uri2 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-222-2222");
   1808 
   1809         // First, test with no contacts on either profile.
   1810         assertEquals(0, getCount(uri1));
   1811 
   1812         // Insert a contact to the primary CP2.
   1813         long rawContactId = ContentUris.parseId(
   1814                 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()));
   1815         DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe");
   1816         insertPhoneNumber(rawContactId, "408-111-1111");
   1817 
   1818         // Insert a contact to the corp CP2, with the same phone number, but with a different name.
   1819         rawContactId = ContentUris.parseId(
   1820                 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues()));
   1821         // Insert a name
   1822         ContentValues cv = cv(
   1823                 Data.RAW_CONTACT_ID, rawContactId,
   1824                 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE,
   1825                 StructuredName.DISPLAY_NAME, "Contact2 Corp",
   1826                 StructuredName.GIVEN_NAME, "Contact2",
   1827                 StructuredName.FAMILY_NAME, "Corp");
   1828         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
   1829 
   1830         // Insert a number
   1831         cv = cv(
   1832                 Data.RAW_CONTACT_ID, rawContactId,
   1833                 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE,
   1834                 Phone.NUMBER, "408-111-1111",
   1835                 Phone.TYPE, Phone.TYPE_HOME);
   1836         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
   1837 
   1838         // Insert one more contact to the corp CP2, with a different number.
   1839         rawContactId = ContentUris.parseId(
   1840                 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues()));
   1841         // Insert a name
   1842         cv = cv(
   1843                 Data.RAW_CONTACT_ID, rawContactId,
   1844                 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE,
   1845                 StructuredName.DISPLAY_NAME, "Contact3 Corp",
   1846                 StructuredName.GIVEN_NAME, "Contact3",
   1847                 StructuredName.FAMILY_NAME, "Corp");
   1848         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
   1849 
   1850         // Insert a number
   1851         cv = cv(
   1852                 Data.RAW_CONTACT_ID, rawContactId,
   1853                 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE,
   1854                 Phone.NUMBER, "408-222-2222",
   1855                 Phone.TYPE, Phone.TYPE_HOME);
   1856         corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv);
   1857 
   1858         // Okay, now execute queries and check the result.
   1859 
   1860         // The first URL hits the contact in the primary CP2.
   1861         // There's also a contact with this phone number in the corp CP2, but that will be ignored.
   1862         Cursor c = mResolver.query(uri1, null, null, null, null);
   1863         try {
   1864             assertEquals(1, c.getCount());
   1865             c.moveToPosition(0);
   1866             assertEquals("Contact1 Doe", c.getString(c.getColumnIndex(PhoneLookup.DISPLAY_NAME)));
   1867 
   1868             // Make sure it has a personal contact ID.
   1869             long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID));
   1870             assertFalse(Contacts.isEnterpriseContactId(contactId));
   1871         } finally {
   1872             c.close();
   1873         }
   1874 
   1875         // Test for the second phone number, which only exists in the corp cp2.
   1876         c = mResolver.query(uri2, null, null, null, null);
   1877         try {
   1878             // This one actually returns 2 identical rows, probably because of the join
   1879             // in phone_lookup.  Callers only care the first row, so returning multiple identical
   1880             // rows should be fine.
   1881             assertTrue(c.getCount() > 0);
   1882             c.moveToPosition(0);
   1883             assertEquals("Contact3 Corp", c.getString(c.getColumnIndex(PhoneLookup.DISPLAY_NAME)));
   1884 
   1885             // Make sure it has a corp contact ID.
   1886             long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID));
   1887             assertTrue(Contacts.isEnterpriseContactId(contactId));
   1888         } finally {
   1889             c.close();
   1890         }
   1891     }
   1892 
   1893     public void testUpgradeToVersion910_CallsDeletedForCorpProfileOnly() throws Exception {
   1894         CallLogProvider provider =
   1895                 (CallLogProvider) addProvider(TestCallLogProvider.class, CallLog.AUTHORITY);
   1896         final ContactsDatabaseHelper helper = provider.getDatabaseHelper(mContext);
   1897         final SQLiteDatabase db = helper.getWritableDatabase();
   1898 
   1899         final ContentValues values = new ContentValues();
   1900         values.put(Calls.NUMBER, "123456789");
   1901         values.put(Calls.DATE, System.currentTimeMillis());
   1902         values.put(Calls.TYPE, Calls.OUTGOING_TYPE);
   1903         values.put(Calls.DURATION, 10000);
   1904 
   1905         mResolver.insert(Calls.CONTENT_URI, values);
   1906         assertEquals(1, getCount(Calls.CONTENT_URI));
   1907 
   1908         helper.upgradeToVersion910(db);
   1909         assertEquals(1, getCount(Calls.CONTENT_URI));
   1910 
   1911         mActor.mockUserManager.myUser = MockUserManager.CORP_USER.id;
   1912         mActor.mockUserManager.setUsers(MockUserManager.CORP_USER);
   1913 
   1914         helper.upgradeToVersion910(db);
   1915 
   1916         // Switch back to the primary user to ensure that the calls table was really cleared, and
   1917         // we are not getting an empty cursor just because of the call log read/write restriction
   1918         // on managed profiles.
   1919         mActor.mockUserManager.myUser = MockUserManager.PRIMARY_USER.id;
   1920         mActor.mockUserManager.setUsers(MockUserManager.PRIMARY_USER);
   1921         assertEquals(0, getCount(Calls.CONTENT_URI));
   1922     }
   1923 
   1924     public void testRewriteCorpPhoneLookup() {
   1925         // 19 columns
   1926         final MatrixCursor c = new MatrixCursor(new String[] {
   1927                 PhoneLookup._ID,
   1928                 PhoneLookup.LOOKUP_KEY,
   1929                 PhoneLookup.DISPLAY_NAME,
   1930                 PhoneLookup.LAST_TIME_CONTACTED,
   1931                 PhoneLookup.TIMES_CONTACTED,
   1932                 PhoneLookup.STARRED,
   1933                 PhoneLookup.IN_DEFAULT_DIRECTORY,
   1934                 PhoneLookup.IN_VISIBLE_GROUP,
   1935                 PhoneLookup.PHOTO_FILE_ID,
   1936                 PhoneLookup.PHOTO_ID,
   1937                 PhoneLookup.PHOTO_URI,
   1938                 PhoneLookup.PHOTO_THUMBNAIL_URI,
   1939                 PhoneLookup.CUSTOM_RINGTONE,
   1940                 PhoneLookup.HAS_PHONE_NUMBER,
   1941                 PhoneLookup.SEND_TO_VOICEMAIL,
   1942                 PhoneLookup.NUMBER,
   1943                 PhoneLookup.TYPE,
   1944                 PhoneLookup.LABEL,
   1945                 PhoneLookup.NORMALIZED_NUMBER
   1946         });
   1947 
   1948         // First, convert and make sure it returns an empty cursor.
   1949         Cursor rewritten = ContactsProvider2.rewriteCorpPhoneLookup(c);
   1950         assertEquals(0, rewritten.getCount());
   1951         assertEquals(19, rewritten.getColumnCount());
   1952 
   1953         c.addRow(new Object[] {
   1954                 1L, // PhoneLookup._ID,
   1955                 null, // PhoneLookup.LOOKUP_KEY,
   1956                 null, // PhoneLookup.DISPLAY_NAME,
   1957                 null, // PhoneLookup.LAST_TIME_CONTACTED,
   1958                 null, // PhoneLookup.TIMES_CONTACTED,
   1959                 null, // PhoneLookup.STARRED,
   1960                 null, // PhoneLookup.IN_DEFAULT_DIRECTORY,
   1961                 null, // PhoneLookup.IN_VISIBLE_GROUP,
   1962                 null, // PhoneLookup.PHOTO_FILE_ID,
   1963                 null, // PhoneLookup.PHOTO_ID,
   1964                 null, // PhoneLookup.PHOTO_URI,
   1965                 null, // PhoneLookup.PHOTO_THUMBNAIL_URI,
   1966                 null, // PhoneLookup.CUSTOM_RINGTONE,
   1967                 null, // PhoneLookup.HAS_PHONE_NUMBER,
   1968                 null, // PhoneLookup.SEND_TO_VOICEMAIL,
   1969                 null, // PhoneLookup.NUMBER,
   1970                 null, // PhoneLookup.TYPE,
   1971                 null, // PhoneLookup.LABEL,
   1972                 null, // PhoneLookup.NORMALIZED_NUMBER
   1973         });
   1974 
   1975         c.addRow(new Object[] {
   1976                 10L, // PhoneLookup._ID,
   1977                 "key", // PhoneLookup.LOOKUP_KEY,
   1978                 "name", // PhoneLookup.DISPLAY_NAME,
   1979                 123, // PhoneLookup.LAST_TIME_CONTACTED,
   1980                 456, // PhoneLookup.TIMES_CONTACTED,
   1981                 1, // PhoneLookup.STARRED,
   1982                 1, // PhoneLookup.IN_DEFAULT_DIRECTORY,
   1983                 1, // PhoneLookup.IN_VISIBLE_GROUP,
   1984                 1001, // PhoneLookup.PHOTO_FILE_ID,
   1985                 1002, // PhoneLookup.PHOTO_ID,
   1986                 "content://a/a", // PhoneLookup.PHOTO_URI,
   1987                 "content://a/b", // PhoneLookup.PHOTO_THUMBNAIL_URI,
   1988                 "content://a/c", // PhoneLookup.CUSTOM_RINGTONE,
   1989                 1, // PhoneLookup.HAS_PHONE_NUMBER,
   1990                 1, // PhoneLookup.SEND_TO_VOICEMAIL,
   1991                 "1234", // PhoneLookup.NUMBER,
   1992                 1, // PhoneLookup.TYPE,
   1993                 "label", // PhoneLookup.LABEL,
   1994                 "+1234", // PhoneLookup.NORMALIZED_NUMBER
   1995         });
   1996         rewritten = ContactsProvider2.rewriteCorpPhoneLookup(c);
   1997         assertEquals(2, rewritten.getCount());
   1998 
   1999         rewritten.moveToPosition(0);
   2000         int column = 0;
   2001         assertEquals(1000000001L, rewritten.getLong(column++)); // We offset ID for corp contacts.
   2002         assertEquals(null, rewritten.getString(column++));
   2003         assertEquals(null, rewritten.getString(column++));
   2004         assertEquals(null, rewritten.getString(column++));
   2005         assertEquals(null, rewritten.getString(column++));
   2006         assertEquals(null, rewritten.getString(column++));
   2007         assertEquals(null, rewritten.getString(column++));
   2008         assertEquals(null, rewritten.getString(column++));
   2009         assertEquals(null, rewritten.getString(column++));
   2010         assertEquals(null, rewritten.getString(column++));
   2011         assertEquals(null, rewritten.getString(column++));
   2012         assertEquals(null, rewritten.getString(column++));
   2013         assertEquals(null, rewritten.getString(column++));
   2014         assertEquals(null, rewritten.getString(column++));
   2015         assertEquals(null, rewritten.getString(column++));
   2016         assertEquals(null, rewritten.getString(column++));
   2017         assertEquals(null, rewritten.getString(column++));
   2018         assertEquals(null, rewritten.getString(column++));
   2019         assertEquals(null, rewritten.getString(column++));
   2020 
   2021 
   2022         rewritten.moveToNext();
   2023         column = 0;
   2024         assertEquals(1000000010L, rewritten.getLong(column++)); // With offset.
   2025         assertEquals("key", rewritten.getString(column++));
   2026         assertEquals("name", rewritten.getString(column++));
   2027         assertEquals(123, rewritten.getInt(column++));
   2028         assertEquals(456, rewritten.getInt(column++));
   2029         assertEquals(1, rewritten.getInt(column++));
   2030         assertEquals(1, rewritten.getInt(column++));
   2031         assertEquals(1, rewritten.getInt(column++));
   2032         assertEquals(null, rewritten.getString(column++)); // photo file id
   2033         assertEquals(null, rewritten.getString(column++)); // photo id
   2034         assertEquals("content://com.android.contacts/contacts_corp/10/display_photo",
   2035                 rewritten.getString(column++));
   2036         assertEquals("content://com.android.contacts/contacts_corp/10/photo",
   2037                 rewritten.getString(column++));
   2038         assertEquals(null, rewritten.getString(column++)); // ringtone
   2039         assertEquals(1, rewritten.getInt(column++));
   2040         assertEquals(1, rewritten.getInt(column++));
   2041         assertEquals("1234", rewritten.getString(column++));
   2042         assertEquals(1, rewritten.getInt(column++));
   2043         assertEquals("label", rewritten.getString(column++));
   2044         assertEquals("+1234", rewritten.getString(column++));
   2045     }
   2046 
   2047     public void testPhoneUpdate() {
   2048         ContentValues values = new ContentValues();
   2049         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   2050         long rawContactId = ContentUris.parseId(rawContactUri);
   2051 
   2052         DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale");
   2053         Uri phoneUri = insertPhoneNumber(rawContactId, "18004664411");
   2054 
   2055         Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411");
   2056         Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664422");
   2057         assertEquals(2, getCount(lookupUri1, null, null));
   2058         assertEquals(0, getCount(lookupUri2, null, null));
   2059 
   2060         values.clear();
   2061         values.put(Phone.NUMBER, "18004664422");
   2062         mResolver.update(phoneUri, values, null, null);
   2063 
   2064         assertEquals(0, getCount(lookupUri1, null, null));
   2065         assertEquals(2, getCount(lookupUri2, null, null));
   2066 
   2067         // Setting number to null will remove the phone lookup record
   2068         values.clear();
   2069         values.putNull(Phone.NUMBER);
   2070         mResolver.update(phoneUri, values, null, null);
   2071 
   2072         assertEquals(0, getCount(lookupUri1, null, null));
   2073         assertEquals(0, getCount(lookupUri2, null, null));
   2074 
   2075         // Let's restore that phone lookup record
   2076         values.clear();
   2077         values.put(Phone.NUMBER, "18004664422");
   2078         mResolver.update(phoneUri, values, null, null);
   2079         assertEquals(0, getCount(lookupUri1, null, null));
   2080         assertEquals(2, getCount(lookupUri2, null, null));
   2081         assertNetworkNotified(true);
   2082     }
   2083 
   2084     /** Tests if {@link Callable#CONTENT_URI} returns both phones and sip addresses. */
   2085     public void testCallablesQuery() {
   2086         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Meghan", "Knox");
   2087         long phoneId1 = ContentUris.parseId(insertPhoneNumber(rawContactId1, "18004664411"));
   2088         long contactId1 = queryContactId(rawContactId1);
   2089 
   2090         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe");
   2091         long sipAddressId2 = ContentUris.parseId(
   2092                 insertSipAddress(rawContactId2, "sip (at) example.com"));
   2093         long contactId2 = queryContactId(rawContactId2);
   2094 
   2095         ContentValues values1 = new ContentValues();
   2096         values1.put(Data._ID, phoneId1);
   2097         values1.put(Data.RAW_CONTACT_ID, rawContactId1);
   2098         values1.put(RawContacts.CONTACT_ID, contactId1);
   2099         values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   2100         values1.put(Phone.NUMBER, "18004664411");
   2101         values1.put(Phone.TYPE, Phone.TYPE_HOME);
   2102         values1.putNull(Phone.LABEL);
   2103         values1.put(Contacts.DISPLAY_NAME, "Meghan Knox");
   2104 
   2105         ContentValues values2 = new ContentValues();
   2106         values2.put(Data._ID, sipAddressId2);
   2107         values2.put(Data.RAW_CONTACT_ID, rawContactId2);
   2108         values2.put(RawContacts.CONTACT_ID, contactId2);
   2109         values2.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
   2110         values2.put(SipAddress.SIP_ADDRESS, "sip (at) example.com");
   2111         values2.put(Contacts.DISPLAY_NAME, "John Doe");
   2112 
   2113         assertEquals(2, getCount(Callable.CONTENT_URI, null, null));
   2114         assertStoredValues(Callable.CONTENT_URI, new ContentValues[] { values1, values2 });
   2115     }
   2116 
   2117     public void testCallablesFilterQuery() {
   2118         testPhonesFilterQueryInter(Callable.CONTENT_FILTER_URI);
   2119     }
   2120 
   2121     public void testEmailsQuery() {
   2122         ContentValues values = new ContentValues();
   2123         values.put(RawContacts.CUSTOM_RINGTONE, "d");
   2124         values.put(RawContacts.SEND_TO_VOICEMAIL, 1);
   2125         values.put(RawContacts.LAST_TIME_CONTACTED, 12345);
   2126         values.put(RawContacts.TIMES_CONTACTED, 54321);
   2127         values.put(RawContacts.STARRED, 1);
   2128 
   2129         Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values);
   2130         final long rawContactId = ContentUris.parseId(rawContactUri);
   2131 
   2132         DataUtil.insertStructuredName(mResolver, rawContactId, "Meghan", "Knox");
   2133         final Uri emailUri = insertEmail(rawContactId, "meghan (at) acme.com");
   2134         final long emailId = ContentUris.parseId(emailUri);
   2135 
   2136         final long contactId = queryContactId(rawContactId);
   2137         values.clear();
   2138         values.put(Data._ID, emailId);
   2139         values.put(Data.RAW_CONTACT_ID, rawContactId);
   2140         values.put(RawContacts.CONTACT_ID, contactId);
   2141         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   2142         values.put(Email.DATA, "meghan (at) acme.com");
   2143         values.put(Email.TYPE, Email.TYPE_HOME);
   2144         values.putNull(Email.LABEL);
   2145         values.put(Contacts.DISPLAY_NAME, "Meghan Knox");
   2146         values.put(Contacts.CUSTOM_RINGTONE, "d");
   2147         values.put(Contacts.SEND_TO_VOICEMAIL, 1);
   2148         values.put(Contacts.LAST_TIME_CONTACTED, 12345);
   2149         values.put(Contacts.TIMES_CONTACTED, 54321);
   2150         values.put(Contacts.STARRED, 1);
   2151 
   2152         assertStoredValues(Email.CONTENT_URI, values);
   2153         assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values);
   2154         assertSelection(Email.CONTENT_URI, values, Data._ID, emailId);
   2155 
   2156         // Check if the provider detects duplicated email addresses.
   2157         final Uri emailUri2 = insertEmail(rawContactId, "meghan (at) acme.com");
   2158         final long emailId2 = ContentUris.parseId(emailUri2);
   2159         final ContentValues values2 = new ContentValues(values);
   2160         values2.put(Data._ID, emailId2);
   2161 
   2162         final Uri dedupeUri = Email.CONTENT_URI.buildUpon()
   2163                 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
   2164                 .build();
   2165 
   2166         // URI with ID should return a correct result.
   2167         assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values);
   2168         assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId), values);
   2169         assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId2), values2);
   2170         assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId2), values2);
   2171 
   2172         assertStoredValues(Email.CONTENT_URI, new ContentValues[] {values, values2});
   2173 
   2174         // If requested to remove duplicates, the query should return just one result,
   2175         // whose _ID won't be deterministic.
   2176         values.remove(Data._ID);
   2177         assertStoredValues(dedupeUri, values);
   2178     }
   2179 
   2180     public void testEmailsLookupQuery() {
   2181         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale");
   2182         insertEmail(rawContactId, "tamale (at) acme.com");
   2183 
   2184         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "tamale (at) acme.com");
   2185         ContentValues values = new ContentValues();
   2186         values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   2187         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   2188         values.put(Email.DATA, "tamale (at) acme.com");
   2189         values.put(Email.TYPE, Email.TYPE_HOME);
   2190         values.putNull(Email.LABEL);
   2191         assertStoredValues(filterUri1, values);
   2192 
   2193         Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "Ta<TaMale (at) acme.com>");
   2194         assertStoredValues(filterUri2, values);
   2195 
   2196         Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "encilada (at) acme.com");
   2197         assertEquals(0, getCount(filterUri3, null, null));
   2198     }
   2199 
   2200     public void testEmailsFilterQuery() {
   2201         long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale",
   2202                 TestUtil.ACCOUNT_1);
   2203         insertEmail(rawContactId1, "tamale (at) acme.com");
   2204         insertEmail(rawContactId1, "tamale (at) acme.com");
   2205 
   2206         long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale",
   2207                 TestUtil.ACCOUNT_2);
   2208         insertEmail(rawContactId2, "tamale (at) acme.com");
   2209 
   2210         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tam");
   2211         ContentValues values = new ContentValues();
   2212         values.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   2213         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   2214         values.put(Email.DATA, "tamale (at) acme.com");
   2215         values.put(Email.TYPE, Email.TYPE_HOME);
   2216         values.putNull(Email.LABEL);
   2217         assertStoredValuesWithProjection(filterUri1, values);
   2218 
   2219         Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot");
   2220         assertStoredValuesWithProjection(filterUri2, values);
   2221 
   2222         Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot tamale");
   2223         assertStoredValuesWithProjection(filterUri3, values);
   2224 
   2225         Uri filterUri4 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tamale@acme");
   2226         assertStoredValuesWithProjection(filterUri4, values);
   2227 
   2228         Uri filterUri5 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "encilada");
   2229         assertEquals(0, getCount(filterUri5, null, null));
   2230     }
   2231 
   2232     /**
   2233      * Tests if ContactsProvider2 returns addresses according to registration order.
   2234      */
   2235     public void testEmailFilterDefaultSortOrder() {
   2236         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
   2237         insertEmail(rawContactId1, "address1 (at) email.com");
   2238         insertEmail(rawContactId1, "address2 (at) email.com");
   2239         insertEmail(rawContactId1, "address3 (at) email.com");
   2240         ContentValues v1 = new ContentValues();
   2241         v1.put(Email.ADDRESS, "address1 (at) email.com");
   2242         ContentValues v2 = new ContentValues();
   2243         v2.put(Email.ADDRESS, "address2 (at) email.com");
   2244         ContentValues v3 = new ContentValues();
   2245         v3.put(Email.ADDRESS, "address3 (at) email.com");
   2246 
   2247         Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
   2248         assertStoredValuesOrderly(filterUri, new ContentValues[]{v1, v2, v3});
   2249     }
   2250 
   2251     /**
   2252      * Tests if ContactsProvider2 returns primary addresses before the other addresses.
   2253      */
   2254     public void testEmailFilterPrimaryAddress() {
   2255         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
   2256         insertEmail(rawContactId1, "address1 (at) email.com");
   2257         insertEmail(rawContactId1, "address2 (at) email.com", true);
   2258         ContentValues v1 = new ContentValues();
   2259         v1.put(Email.ADDRESS, "address1 (at) email.com");
   2260         ContentValues v2 = new ContentValues();
   2261         v2.put(Email.ADDRESS, "address2 (at) email.com");
   2262 
   2263         Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
   2264         assertStoredValuesOrderly(filterUri, new ContentValues[] { v2, v1 });
   2265     }
   2266 
   2267     /**
   2268      * Tests if ContactsProvider2 has email address associated with a primary account before the
   2269      * other address.
   2270      */
   2271     public void testEmailFilterPrimaryAccount() {
   2272         long rawContactId1 = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1);
   2273         insertEmail(rawContactId1, "account1 (at) email.com");
   2274         long rawContactId2 = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_2);
   2275         insertEmail(rawContactId2, "account2 (at) email.com");
   2276         ContentValues v1 = new ContentValues();
   2277         v1.put(Email.ADDRESS, "account1 (at) email.com");
   2278         ContentValues v2 = new ContentValues();
   2279         v2.put(Email.ADDRESS, "account2 (at) email.com");
   2280 
   2281         Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
   2282                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_1.name)
   2283                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, TestUtil.ACCOUNT_1.type)
   2284                 .build();
   2285         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2 });
   2286 
   2287         Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
   2288                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_2.name)
   2289                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, TestUtil.ACCOUNT_2.type)
   2290                 .build();
   2291         assertStoredValuesOrderly(filterUri2, new ContentValues[] { v2, v1 });
   2292 
   2293         // Just with PRIMARY_ACCOUNT_NAME
   2294         Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
   2295                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_1.name)
   2296                 .build();
   2297         assertStoredValuesOrderly(filterUri3, new ContentValues[]{v1, v2});
   2298 
   2299         Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
   2300                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_2.name)
   2301                 .build();
   2302         assertStoredValuesOrderly(filterUri4, new ContentValues[] { v2, v1 });
   2303     }
   2304 
   2305     /**
   2306      * Test emails with the same domain as primary account are ordered first.
   2307      */
   2308     public void testEmailFilterSameDomainAccountOrder() {
   2309         final Account account = new Account("tester (at) email.com", "not_used");
   2310         final long rawContactId = RawContactUtil.createRawContact(mResolver, account);
   2311         insertEmail(rawContactId, "account1 (at) testemail.com");
   2312         insertEmail(rawContactId, "account1 (at) email.com");
   2313 
   2314         final ContentValues v1 = cv(Email.ADDRESS, "account1 (at) testemail.com");
   2315         final ContentValues v2 = cv(Email.ADDRESS, "account1 (at) email.com");
   2316 
   2317         Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc")
   2318                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, account.name)
   2319                 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, account.type)
   2320                 .build();
   2321         assertStoredValuesOrderly(filterUri1, v2, v1);
   2322     }
   2323 
   2324     /**
   2325      * Test "default" emails are sorted above emails used last.
   2326      */
   2327     public void testEmailFilterSuperPrimaryOverUsageSort() {
   2328         final long rawContactId = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1);
   2329         final Uri emailUri1 = insertEmail(rawContactId, "account1 (at) testemail.com");
   2330         final Uri emailUri2 = insertEmail(rawContactId, "account2 (at) testemail.com");
   2331         insertEmail(rawContactId, "account3 (at) testemail.com", true, true);
   2332 
   2333         // Update account1 and account 2 to have higher usage.
   2334         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1);
   2335         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1);
   2336         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri2);
   2337 
   2338         final ContentValues v1 = cv(Email.ADDRESS, "account1 (at) testemail.com");
   2339         final ContentValues v2 = cv(Email.ADDRESS, "account2 (at) testemail.com");
   2340         final ContentValues v3 = cv(Email.ADDRESS, "account3 (at) testemail.com");
   2341 
   2342         // Test that account 3 is first even though account 1 and 2 have higher usage.
   2343         Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "acc");
   2344         assertStoredValuesOrderly(filterUri, v3, v1, v2);
   2345     }
   2346 
   2347     /**
   2348      * Test primary emails are sorted below emails used last.
   2349      *
   2350      * primary may be set without super primary.  Only super primary indicates "default" in the
   2351      * contact ui.
   2352      */
   2353     public void testEmailFilterUsageOverPrimarySort() {
   2354         final long rawContactId = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1);
   2355         final Uri emailUri1 = insertEmail(rawContactId, "account1 (at) testemail.com");
   2356         final Uri emailUri2 = insertEmail(rawContactId, "account2 (at) testemail.com");
   2357         insertEmail(rawContactId, "account3 (at) testemail.com", true);
   2358 
   2359         // Update account1 and account 2 to have higher usage.
   2360         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1);
   2361         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1);
   2362         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri2);
   2363 
   2364         final ContentValues v1 = cv(Email.ADDRESS, "account1 (at) testemail.com");
   2365         final ContentValues v2 = cv(Email.ADDRESS, "account2 (at) testemail.com");
   2366         final ContentValues v3 = cv(Email.ADDRESS, "account3 (at) testemail.com");
   2367 
   2368         // Test that account 3 is first even though account 1 and 2 have higher usage.
   2369         Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "acc");
   2370         assertStoredValuesOrderly(filterUri, v1, v2, v3);
   2371     }
   2372 
   2373     /** Tests {@link DataUsageFeedback} correctly promotes a data row instead of a raw contact. */
   2374     public void testEmailFilterSortOrderWithFeedback() {
   2375         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
   2376         String address1 = "address1 (at) email.com";
   2377         insertEmail(rawContactId1, address1);
   2378 
   2379         long rawContactId2 = RawContactUtil.createRawContact(mResolver);
   2380         String address2 = "address2 (at) email.com";
   2381         insertEmail(rawContactId2, address2);
   2382         String address3 = "address3 (at) email.com";
   2383         ContentUris.parseId(insertEmail(rawContactId2, address3));
   2384 
   2385         ContentValues v1 = new ContentValues();
   2386         v1.put(Email.ADDRESS, "address1 (at) email.com");
   2387         ContentValues v2 = new ContentValues();
   2388         v2.put(Email.ADDRESS, "address2 (at) email.com");
   2389         ContentValues v3 = new ContentValues();
   2390         v3.put(Email.ADDRESS, "address3 (at) email.com");
   2391 
   2392         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
   2393         Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
   2394                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
   2395                         DataUsageFeedback.USAGE_TYPE_CALL)
   2396                 .build();
   2397         Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
   2398                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
   2399                         DataUsageFeedback.USAGE_TYPE_LONG_TEXT)
   2400                 .build();
   2401         Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address")
   2402                 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
   2403                         DataUsageFeedback.USAGE_TYPE_SHORT_TEXT)
   2404                 .build();
   2405         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2, v3 });
   2406         assertStoredValuesOrderly(filterUri2, new ContentValues[] { v1, v2, v3 });
   2407         assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2, v3 });
   2408         assertStoredValuesOrderly(filterUri4, new ContentValues[] { v1, v2, v3 });
   2409 
   2410         sendFeedback(address3, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, v3);
   2411 
   2412         assertStoredValuesWithProjection(RawContacts.CONTENT_URI,
   2413                 cv(RawContacts._ID, rawContactId1,
   2414                         RawContacts.TIMES_CONTACTED, 0
   2415                         ),
   2416                 cv(RawContacts._ID, rawContactId2,
   2417                         RawContacts.TIMES_CONTACTED, 1
   2418                         )
   2419                 );
   2420 
   2421         // account3 (at) email.com should be the first.
   2422         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v3, v1, v2 });
   2423         assertStoredValuesOrderly(filterUri3, new ContentValues[] { v3, v1, v2 });
   2424     }
   2425 
   2426     /**
   2427      * Tests {@link DataUsageFeedback} correctly bucketize contacts using each
   2428      * {@link DataUsageStatColumns#LAST_TIME_USED}
   2429      */
   2430     public void testEmailFilterSortOrderWithOldHistory() {
   2431         long rawContactId1 = RawContactUtil.createRawContact(mResolver);
   2432         long dataId1 = ContentUris.parseId(insertEmail(rawContactId1, "address1 (at) email.com"));
   2433         long dataId2 = ContentUris.parseId(insertEmail(rawContactId1, "address2 (at) email.com"));
   2434         long dataId3 = ContentUris.parseId(insertEmail(rawContactId1, "address3 (at) email.com"));
   2435         long dataId4 = ContentUris.parseId(insertEmail(rawContactId1, "address4 (at) email.com"));
   2436 
   2437         Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address");
   2438 
   2439         ContentValues v1 = new ContentValues();
   2440         v1.put(Email.ADDRESS, "address1 (at) email.com");
   2441         ContentValues v2 = new ContentValues();
   2442         v2.put(Email.ADDRESS, "address2 (at) email.com");
   2443         ContentValues v3 = new ContentValues();
   2444         v3.put(Email.ADDRESS, "address3 (at) email.com");
   2445         ContentValues v4 = new ContentValues();
   2446         v4.put(Email.ADDRESS, "address4 (at) email.com");
   2447 
   2448         final ContactsProvider2 provider = (ContactsProvider2) getProvider();
   2449 
   2450         long nowInMillis = System.currentTimeMillis();
   2451         long yesterdayInMillis = (nowInMillis - 24 * 60 * 60 * 1000);
   2452         long sevenDaysAgoInMillis = (nowInMillis - 7 * 24 * 60 * 60 * 1000);
   2453         long oneYearAgoInMillis = (nowInMillis - 365L * 24 * 60 * 60 * 1000);
   2454 
   2455         // address4 is contacted just once yesterday.
   2456         provider.updateDataUsageStat(Arrays.asList(dataId4),
   2457                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, yesterdayInMillis);
   2458 
   2459         // address3 is contacted twice 1 week ago.
   2460         provider.updateDataUsageStat(Arrays.asList(dataId3),
   2461                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis);
   2462         provider.updateDataUsageStat(Arrays.asList(dataId3),
   2463                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis);
   2464 
   2465         // address2 is contacted three times 1 year ago.
   2466         provider.updateDataUsageStat(Arrays.asList(dataId2),
   2467                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
   2468         provider.updateDataUsageStat(Arrays.asList(dataId2),
   2469                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
   2470         provider.updateDataUsageStat(Arrays.asList(dataId2),
   2471                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis);
   2472 
   2473         // auto-complete should prefer recently contacted methods
   2474         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v4, v3, v2, v1 });
   2475 
   2476         // Pretend address2 is contacted right now
   2477         provider.updateDataUsageStat(Arrays.asList(dataId2),
   2478                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis);
   2479 
   2480         // Now address2 is the most recently used address
   2481         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v4, v3, v1 });
   2482 
   2483         // Pretend address1 is contacted right now
   2484         provider.updateDataUsageStat(Arrays.asList(dataId1),
   2485                 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis);
   2486 
   2487         // address2 is preferred to address1 as address2 is used 4 times in total
   2488         assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v1, v4, v3 });
   2489     }
   2490 
   2491     public void testPostalsQuery() {
   2492         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Alice", "Nextore");
   2493         Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");
   2494         final long dataId = ContentUris.parseId(dataUri);
   2495 
   2496         final long contactId = queryContactId(rawContactId);
   2497         ContentValues values = new ContentValues();
   2498         values.put(Data._ID, dataId);
   2499         values.put(Data.RAW_CONTACT_ID, rawContactId);
   2500         values.put(RawContacts.CONTACT_ID, contactId);
   2501         values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
   2502         values.put(StructuredPostal.FORMATTED_ADDRESS, "1600 Amphiteatre Ave, Mountain View");
   2503         values.put(Contacts.DISPLAY_NAME, "Alice Nextore");
   2504 
   2505         assertStoredValues(StructuredPostal.CONTENT_URI, values);
   2506         assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId),
   2507                 values);
   2508         assertSelection(StructuredPostal.CONTENT_URI, values, Data._ID, dataId);
   2509 
   2510         // Check if the provider detects duplicated addresses.
   2511         Uri dataUri2 = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View");
   2512         final long dataId2 = ContentUris.parseId(dataUri2);
   2513         final ContentValues values2 = new ContentValues(values);
   2514         values2.put(Data._ID, dataId2);
   2515 
   2516         final Uri dedupeUri = StructuredPostal.CONTENT_URI.buildUpon()
   2517                 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
   2518                 .build();
   2519 
   2520         // URI with ID should return a correct result.
   2521         assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId),
   2522                 values);
   2523         assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId), values);
   2524         assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId2),
   2525                 values2);
   2526         assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId2), values2);
   2527 
   2528         assertStoredValues(StructuredPostal.CONTENT_URI, new ContentValues[] {values, values2});
   2529 
   2530         // If requested to remove duplicates, the query should return just one result,
   2531         // whose _ID won't be deterministic.
   2532         values.remove(Data._ID);
   2533         assertStoredValues(dedupeUri, values);
   2534     }
   2535 
   2536     public void testDataContentUriInvisibleQuery() {
   2537         final ContentValues values = new ContentValues();
   2538         final long contactId = createContact(values, "John", "Doe",
   2539                 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   2540                         StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
   2541 
   2542         final Uri uri = Data.CONTENT_URI.buildUpon().
   2543                 appendQueryParameter(Data.VISIBLE_CONTACTS_ONLY, "true").build();
   2544         assertEquals(4, getCount(uri, null, null));
   2545 
   2546         markInvisible(contactId);
   2547 
   2548         assertEquals(0, getCount(uri, null, null));
   2549     }
   2550 
   2551     public void testInDefaultDirectoryData() {
   2552         final ContentValues values = new ContentValues();
   2553         final long contactId = createContact(values, "John", "Doe",
   2554                 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   2555                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   2556 
   2557         final StringBuilder query = new StringBuilder()
   2558                 .append(Data.MIMETYPE).append("='").append(Email.CONTENT_ITEM_TYPE)
   2559                 .append("' AND ").append(Email.DATA).append("=? AND ")
   2560                 .append(Contacts.IN_DEFAULT_DIRECTORY).append("=1");
   2561 
   2562         assertEquals(1,
   2563                 getCount(Email.CONTENT_URI, query.toString(), new String[]{"goog411 (at) acme.com"}));
   2564 
   2565         // Fire!
   2566         markInvisible(contactId);
   2567 
   2568         // Verify: making a contact visible changes the IN_DEFAULT_DIRECTORY data value.
   2569         assertEquals(0,
   2570                 getCount(Email.CONTENT_URI, query.toString(), new String[]{"goog411 (at) acme.com"}));
   2571     }
   2572 
   2573     public void testContactablesQuery() {
   2574         final long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot",
   2575                 "Tamale");
   2576 
   2577         insertPhoneNumber(rawContactId, "510-123-5769");
   2578         insertEmail(rawContactId, "tamale (at) acme.com");
   2579 
   2580         final ContentValues cv1 = new ContentValues();
   2581         cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   2582         cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   2583         cv1.put(Email.DATA, "tamale (at) acme.com");
   2584         cv1.put(Email.TYPE, Email.TYPE_HOME);
   2585         cv1.putNull(Email.LABEL);
   2586 
   2587         final ContentValues cv2 = new ContentValues();
   2588         cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   2589         cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   2590         cv2.put(Phone.DATA, "510-123-5769");
   2591         cv2.put(Phone.TYPE, Phone.TYPE_HOME);
   2592         cv2.putNull(Phone.LABEL);
   2593 
   2594         final Uri filterUri0 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "");
   2595         assertEquals(0, getCount(filterUri0, null, null));
   2596 
   2597         final Uri filterUri1 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale");
   2598         assertStoredValues(filterUri1, cv1, cv2);
   2599 
   2600         final Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "hot");
   2601         assertStoredValues(filterUri2, cv1, cv2);
   2602 
   2603         final Uri filterUri3 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale@ac");
   2604         assertStoredValues(filterUri3, cv1, cv2);
   2605 
   2606         final Uri filterUri4 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "510");
   2607         assertStoredValues(filterUri4, cv1, cv2);
   2608 
   2609         final Uri filterUri5 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "cold");
   2610         assertEquals(0, getCount(filterUri5, null, null));
   2611 
   2612         final Uri filterUri6 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI,
   2613                 "tamale@google");
   2614         assertEquals(0, getCount(filterUri6, null, null));
   2615 
   2616         final Uri filterUri7 = Contactables.CONTENT_URI;
   2617         assertStoredValues(filterUri7, cv1, cv2);
   2618     }
   2619 
   2620     public void testContactablesMultipleQuery() {
   2621 
   2622         final long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot",
   2623                 "Tamale");
   2624         insertPhoneNumber(rawContactId, "510-123-5769");
   2625         insertEmail(rawContactId, "tamale (at) acme.com");
   2626         insertEmail(rawContactId, "hot (at) google.com");
   2627 
   2628         final long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Cold",
   2629                 "Tamago");
   2630         insertEmail(rawContactId2, "eggs (at) farmers.org");
   2631 
   2632         final long rawContactId3 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe");
   2633         insertPhoneNumber(rawContactId3, "518-354-1111");
   2634         insertEmail(rawContactId3, "doeadeer (at) afemaledeer.com");
   2635 
   2636         final ContentValues cv1 = new ContentValues();
   2637         cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   2638         cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   2639         cv1.put(Email.DATA, "tamale (at) acme.com");
   2640         cv1.put(Email.TYPE, Email.TYPE_HOME);
   2641         cv1.putNull(Email.LABEL);
   2642 
   2643         final ContentValues cv2 = new ContentValues();
   2644         cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   2645         cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   2646         cv2.put(Phone.DATA, "510-123-5769");
   2647         cv2.put(Phone.TYPE, Phone.TYPE_HOME);
   2648         cv2.putNull(Phone.LABEL);
   2649 
   2650         final ContentValues cv3 = new ContentValues();
   2651         cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale");
   2652         cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   2653         cv3.put(Email.DATA, "hot (at) google.com");
   2654         cv3.put(Email.TYPE, Email.TYPE_HOME);
   2655         cv3.putNull(Email.LABEL);
   2656 
   2657         final ContentValues cv4 = new ContentValues();
   2658         cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago");
   2659         cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   2660         cv4.put(Email.DATA, "eggs (at) farmers.org");
   2661         cv4.put(Email.TYPE, Email.TYPE_HOME);
   2662         cv4.putNull(Email.LABEL);
   2663 
   2664         final ContentValues cv5 = new ContentValues();
   2665         cv5.put(Contacts.DISPLAY_NAME, "John Doe");
   2666         cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   2667         cv5.put(Email.DATA, "doeadeer (at) afemaledeer.com");
   2668         cv5.put(Email.TYPE, Email.TYPE_HOME);
   2669         cv5.putNull(Email.LABEL);
   2670 
   2671         final ContentValues cv6 = new ContentValues();
   2672         cv6.put(Contacts.DISPLAY_NAME, "John Doe");
   2673         cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   2674         cv6.put(Phone.DATA, "518-354-1111");
   2675         cv6.put(Phone.TYPE, Phone.TYPE_HOME);
   2676         cv6.putNull(Phone.LABEL);
   2677 
   2678         final Uri filterUri1 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale");
   2679 
   2680         assertStoredValues(filterUri1, cv1, cv2, cv3);
   2681 
   2682         final Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "hot");
   2683         assertStoredValues(filterUri2, cv1, cv2, cv3);
   2684 
   2685         final Uri filterUri3 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tam");
   2686         assertStoredValues(filterUri3, cv1, cv2, cv3, cv4);
   2687 
   2688         final Uri filterUri4 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "518");
   2689         assertStoredValues(filterUri4, cv5, cv6);
   2690 
   2691         final Uri filterUri5 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "doe");
   2692         assertStoredValues(filterUri5, cv5, cv6);
   2693 
   2694         final Uri filterUri6 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "51");
   2695         assertStoredValues(filterUri6, cv1, cv2, cv3, cv5, cv6);
   2696 
   2697         final Uri filterUri7 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI,
   2698                 "tamale@google");
   2699         assertEquals(0, getCount(filterUri7, null, null));
   2700 
   2701         final Uri filterUri8 = Contactables.CONTENT_URI;
   2702         assertStoredValues(filterUri8, cv1, cv2, cv3, cv4, cv5, cv6);
   2703 
   2704         // test VISIBLE_CONTACTS_ONLY boolean parameter
   2705         final Uri filterUri9 = filterUri6.buildUpon().appendQueryParameter(
   2706                 Contactables.VISIBLE_CONTACTS_ONLY, "true").build();
   2707         assertStoredValues(filterUri9, cv1, cv2, cv3, cv5, cv6);
   2708         // mark Hot Tamale as invisible - cv1, cv2, and cv3 should no longer be in the cursor
   2709         markInvisible(queryContactId(rawContactId));
   2710         assertStoredValues(filterUri9, cv5, cv6);
   2711     }
   2712 
   2713 
   2714     public void testQueryContactData() {
   2715         ContentValues values = new ContentValues();
   2716         long contactId = createContact(values, "John", "Doe",
   2717                 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   2718                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
   2719         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   2720 
   2721         assertStoredValues(contactUri, values);
   2722         assertSelection(Contacts.CONTENT_URI, values, Contacts._ID, contactId);
   2723     }
   2724 
   2725     public void testQueryContactWithStatusUpdate() {
   2726         ContentValues values = new ContentValues();
   2727         long contactId = createContact(values, "John", "Doe",
   2728                 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   2729                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   2730         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
   2731         values.put(Contacts.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
   2732         Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
   2733         assertStoredValuesWithProjection(contactUri, values);
   2734         assertSelectionWithProjection(Contacts.CONTENT_URI, values, Contacts._ID, contactId);
   2735     }
   2736 
   2737     public void testQueryContactFilterByName() {
   2738         ContentValues values = new ContentValues();
   2739         long rawContactId = createRawContact(values, "18004664411",
   2740                 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   2741                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
   2742                 StatusUpdates.CAPABILITY_HAS_VOICE);
   2743 
   2744         ContentValues nameValues = new ContentValues();
   2745         nameValues.put(StructuredName.GIVEN_NAME, "Stu");
   2746         nameValues.put(StructuredName.FAMILY_NAME, "Goulash");
   2747         nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "goo");
   2748         nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "LASH");
   2749         Uri nameUri = DataUtil.insertStructuredName(mResolver, rawContactId, nameValues);
   2750 
   2751         long contactId = queryContactId(rawContactId);
   2752         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
   2753 
   2754         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goulash");
   2755         assertStoredValuesWithProjection(filterUri1, values);
   2756 
   2757         assertContactFilter(contactId, "goolash");
   2758         assertContactFilter(contactId, "lash");
   2759 
   2760         assertContactFilterNoResult("goolish");
   2761 
   2762         // Phonetic name with given/family reversed should not match
   2763         assertContactFilterNoResult("lashgoo");
   2764 
   2765         nameValues.clear();
   2766         nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "ga");
   2767         nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "losh");
   2768 
   2769         mResolver.update(nameUri, nameValues, null, null);
   2770 
   2771         assertContactFilter(contactId, "galosh");
   2772 
   2773         assertContactFilterNoResult("goolish");
   2774     }
   2775 
   2776     public void testQueryContactFilterByEmailAddress() {
   2777         ContentValues values = new ContentValues();
   2778         long rawContactId = createRawContact(values, "18004664411",
   2779                 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   2780                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
   2781                 StatusUpdates.CAPABILITY_HAS_VOICE);
   2782 
   2783         DataUtil.insertStructuredName(mResolver, rawContactId, "James", "Bond");
   2784 
   2785         long contactId = queryContactId(rawContactId);
   2786         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
   2787 
   2788         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goog411 (at) acme.com");
   2789         assertStoredValuesWithProjection(filterUri1, values);
   2790 
   2791         assertContactFilter(contactId, "goog");
   2792         assertContactFilter(contactId, "goog411");
   2793         assertContactFilter(contactId, "goog411@");
   2794         assertContactFilter(contactId, "goog411@acme");
   2795         assertContactFilter(contactId, "goog411 (at) acme.com");
   2796 
   2797         assertContactFilterNoResult("goog411 (at) acme.combo");
   2798         assertContactFilterNoResult("goog411 (at) le.com");
   2799         assertContactFilterNoResult("goolish");
   2800     }
   2801 
   2802     public void testQueryContactFilterByPhoneNumber() {
   2803         ContentValues values = new ContentValues();
   2804         long rawContactId = createRawContact(values, "18004664411",
   2805                 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0,
   2806                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO |
   2807                 StatusUpdates.CAPABILITY_HAS_VOICE);
   2808 
   2809         DataUtil.insertStructuredName(mResolver, rawContactId, "James", "Bond");
   2810 
   2811         long contactId = queryContactId(rawContactId);
   2812         values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE);
   2813 
   2814         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "18004664411");
   2815         assertStoredValuesWithProjection(filterUri1, values);
   2816 
   2817         assertContactFilter(contactId, "18004664411");
   2818         assertContactFilter(contactId, "1800466");
   2819         assertContactFilter(contactId, "+18004664411");
   2820         assertContactFilter(contactId, "8004664411");
   2821 
   2822         assertContactFilterNoResult("78004664411");
   2823         assertContactFilterNoResult("18004664412");
   2824         assertContactFilterNoResult("8884664411");
   2825     }
   2826 
   2827     /**
   2828      * Checks ContactsProvider2 works well with strequent Uris. The provider should return starred
   2829      * contacts and frequently used contacts.
   2830      */
   2831     public void testQueryContactStrequent() {
   2832         ContentValues values1 = new ContentValues();
   2833         final String email1 = "a (at) acme.com";
   2834         final String phoneNumber1 = "18004664411";
   2835         final int timesContacted1 = 0;
   2836         createContact(values1, "Noah", "Tever", phoneNumber1,
   2837                 email1, StatusUpdates.OFFLINE, timesContacted1, 0, 0,
   2838                 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO);
   2839         final String phoneNumber2 = "18004664412";
   2840         ContentValues values2 = new ContentValues();
   2841         createContact(values2, "Sam", "Times", phoneNumber2,
   2842                 "b (at) acme.com", StatusUpdates.INVISIBLE, 3, 0, 0,
   2843                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   2844         ContentValues values3 = new ContentValues();
   2845         final String phoneNumber3 = "18004664413";
   2846         final int timesContacted3 = 5;
   2847         createContact(values3, "Lotta", "Calling", phoneNumber3,
   2848                 "c (at) acme.com", StatusUpdates.AWAY, timesContacted3, 0, 0,
   2849                 StatusUpdates.CAPABILITY_HAS_VIDEO);
   2850         ContentValues values4 = new ContentValues();
   2851         final long rawContactId4 = createRawContact(values4, "Fay", "Veritt", null,
   2852                 "d (at) acme.com", StatusUpdates.AVAILABLE, 0, 1, 0,
   2853                 StatusUpdates.CAPABILITY_HAS_VIDEO | StatusUpdates.CAPABILITY_HAS_VOICE);
   2854 
   2855         // Starred contacts should be returned. TIMES_CONTACTED should be ignored and only data
   2856         // usage feedback should be used for "frequently contacted" listing.
   2857         assertStoredValues(Contacts.CONTENT_STREQUENT_URI, values4);
   2858 
   2859         // Send feedback for the 3rd phone number, pretending we called that person via phone.
   2860         sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
   2861 
   2862         // After the feedback, 3rd contact should be shown after starred one.
   2863         assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI,
   2864                 new ContentValues[] { values4, values3 });
   2865 
   2866         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
   2867         // Twice.
   2868         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
   2869 
   2870         // After the feedback, 1st and 3rd contacts should be shown after starred one.
   2871         assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI,
   2872                 new ContentValues[] { values4, values1, values3 });
   2873 
   2874         // With phone-only parameter, 1st and 4th contacts shouldn't be returned because:
   2875         // 1st: feedbacks are only about email, not about phone call.
   2876         // 4th: it has no phone number though starred.
   2877         Uri phoneOnlyStrequentUri = Contacts.CONTENT_STREQUENT_URI.buildUpon()
   2878                 .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true")
   2879                 .build();
   2880         assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { values3 });
   2881 
   2882         // Now the 4th contact has three phone numbers, one of which is called twice and
   2883         // the other once
   2884         final String phoneNumber4 = "18004664414";
   2885         final String phoneNumber5 = "18004664415";
   2886         final String phoneNumber6 = "18004664416";
   2887         insertPhoneNumber(rawContactId4, phoneNumber4);
   2888         insertPhoneNumber(rawContactId4, phoneNumber5);
   2889         insertPhoneNumber(rawContactId4, phoneNumber6);
   2890         values3.put(Phone.NUMBER, phoneNumber3);
   2891         values4.put(Phone.NUMBER, phoneNumber4);
   2892 
   2893         sendFeedback(phoneNumber5, DataUsageFeedback.USAGE_TYPE_CALL, values4);
   2894         sendFeedback(phoneNumber5, DataUsageFeedback.USAGE_TYPE_CALL, values4);
   2895         sendFeedback(phoneNumber6, DataUsageFeedback.USAGE_TYPE_CALL, values4);
   2896 
   2897         // Create a ContentValues object representing the second phone number of contact 4
   2898         final ContentValues values5 = new ContentValues(values4);
   2899         values5.put(Phone.NUMBER, phoneNumber5);
   2900 
   2901         // Create a ContentValues object representing the third phone number of contact 4
   2902         final ContentValues values6 = new ContentValues(values4);
   2903         values6.put(Phone.NUMBER, phoneNumber6);
   2904 
   2905         // Phone only strequent should return all phone numbers belonging to the 4th contact,
   2906         // and then contact 3.
   2907         assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] {values5, values6,
   2908                 values4, values3});
   2909 
   2910         // Send feedback for the 2rd phone number, pretending we send the person a SMS message.
   2911         sendFeedback(phoneNumber2, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1);
   2912 
   2913         // SMS feedback shouldn't affect phone-only results.
   2914         assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] {values5, values6,
   2915                 values4, values3});
   2916 
   2917         values4.remove(Phone.NUMBER);
   2918         Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_STREQUENT_FILTER_URI, "fay");
   2919         assertStoredValues(filterUri, values4);
   2920     }
   2921 
   2922     public void testQueryContactStrequentFrequentOrder() {
   2923         // Prepare test data
   2924         final long rid1 = RawContactUtil.createRawContact(mResolver);
   2925         final long did1 = ContentUris.parseId(insertPhoneNumber(rid1, "1"));
   2926         final long did1e = ContentUris.parseId(insertEmail(rid1, "1 (at) email.com"));
   2927 
   2928         final long rid2 = RawContactUtil.createRawContact(mResolver);
   2929         final long did2 = ContentUris.parseId(insertPhoneNumber(rid2, "2"));
   2930 
   2931         final long rid3 = RawContactUtil.createRawContact(mResolver);
   2932         final long did3 = ContentUris.parseId(insertPhoneNumber(rid3, "3"));
   2933 
   2934         final long rid4 = RawContactUtil.createRawContact(mResolver);
   2935         final long did4 = ContentUris.parseId(insertPhoneNumber(rid4, "4"));
   2936 
   2937         final long rid5 = RawContactUtil.createRawContact(mResolver);
   2938         final long did5 = ContentUris.parseId(insertPhoneNumber(rid5, "5"));
   2939 
   2940         final long rid6 = RawContactUtil.createRawContact(mResolver);
   2941         final long did6 = ContentUris.parseId(insertPhoneNumber(rid6, "6"));
   2942 
   2943         final long rid7 = RawContactUtil.createRawContact(mResolver);
   2944         final long did7 = ContentUris.parseId(insertPhoneNumber(rid7, "7"));
   2945 
   2946         final long rid8 = RawContactUtil.createRawContact(mResolver);
   2947         final long did8 = ContentUris.parseId(insertPhoneNumber(rid8, "8"));
   2948 
   2949         final long cid1 = queryContactId(rid1);
   2950         final long cid2 = queryContactId(rid2);
   2951         final long cid3 = queryContactId(rid3);
   2952         final long cid4 = queryContactId(rid4);
   2953         final long cid5 = queryContactId(rid5);
   2954         final long cid6 = queryContactId(rid6);
   2955         final long cid7 = queryContactId(rid7);
   2956         final long cid8 = queryContactId(rid8);
   2957 
   2958         // Make sure they aren't aggregated.
   2959         EvenMoreAsserts.assertUnique(cid1, cid2, cid3, cid4, cid5, cid6, cid7, cid8);
   2960 
   2961         // Prepare the clock
   2962         sMockClock.install();
   2963 
   2964         // We check the timestamp in SQL, which doesn't know about the MockClock.  So we need to
   2965         // use the  actual (roughly) time.
   2966 
   2967         final long nowInMillis = System.currentTimeMillis();
   2968         final long oneDayAgoInMillis = (nowInMillis - 24L * 60 * 60 * 1000);
   2969         final long fourDaysAgoInMillis = (nowInMillis - 4L * 24 * 60 * 60 * 1000);
   2970         final long eightDaysAgoInMillis = (nowInMillis - 8L * 24 * 60 * 60 * 1000);
   2971         final long fifteenDaysAgoInMillis = (nowInMillis - 15L * 24 * 60 * 60 * 1000);
   2972         // All contacts older than 30 days will not be included in frequents
   2973         final long thirtyOneDaysAgoInMillis = (nowInMillis - 31L * 24 * 60 * 60 * 1000);
   2974 
   2975         // Contacts in this bucket are considered more than 30 days old
   2976         sMockClock.setCurrentTimeMillis(thirtyOneDaysAgoInMillis);
   2977 
   2978         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did1, did2);
   2979         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did1);
   2980 
   2981         // Contacts in this bucket are considered more than 14 days old
   2982         sMockClock.setCurrentTimeMillis(fifteenDaysAgoInMillis);
   2983 
   2984         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did3, did4);
   2985         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did3);
   2986 
   2987         // Contacts in this bucket are considered more than 7 days old
   2988         sMockClock.setCurrentTimeMillis(eightDaysAgoInMillis);
   2989 
   2990         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did5, did6);
   2991         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did5);
   2992 
   2993         // Contact cid1 again, but it's an email, not a phone call.
   2994         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1e);
   2995 
   2996         // Contacts in this bucket are considered more than 3 days old
   2997         sMockClock.setCurrentTimeMillis(fourDaysAgoInMillis);
   2998 
   2999         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did7);
   3000         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did7);
   3001 
   3002 
   3003         // Contacts in this bucket are considered less than 3 days old
   3004         sMockClock.setCurrentTimeMillis(oneDayAgoInMillis);
   3005 
   3006         updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did8);
   3007 
   3008         sMockClock.setCurrentTimeMillis(nowInMillis);
   3009 
   3010         // Check the order -- The regular frequent, which is contact based.
   3011         // Note because we contacted cid1 8 days ago, it's been contacted 3 times, so it comes
   3012         // before cid5 and cid6, which were contacted at the same time.
   3013         // cid2 will not show up because it was contacted more than 30 days ago
   3014 
   3015         assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI,
   3016                 cv(Contacts._ID, cid8),
   3017                 cv(Contacts._ID, cid7),
   3018                 cv(Contacts._ID, cid1),
   3019                 cv(Contacts._ID, cid5),
   3020                 cv(Contacts._ID, cid6),
   3021                 cv(Contacts._ID, cid3),
   3022                 cv(Contacts._ID, cid4));
   3023 
   3024         // Check the order -- phone only frequent, which is data based.
   3025         // Note this is based on data, and only looks at phone numbers, so the order is different
   3026         // now.
   3027         // did1, did2 will not show up because they were used to make calls more than 30 days ago.
   3028         assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI.buildUpon()
   3029                     .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "1").build(),
   3030                 cv(Data._ID, did8),
   3031                 cv(Data._ID, did7),
   3032                 cv(Data._ID, did5),
   3033                 cv(Data._ID, did6),
   3034                 cv(Data._ID, did3),
   3035                 cv(Data._ID, did4));
   3036     }
   3037 
   3038     /**
   3039      * Checks ContactsProvider2 works well with frequent Uri. The provider should return frequently
   3040      * contacted person ordered by number of times contacted.
   3041      */
   3042     public void testQueryContactFrequent() {
   3043         ContentValues values1 = new ContentValues();
   3044         final String email1 = "a (at) acme.com";
   3045         createContact(values1, "Noah", "Tever", "18004664411",
   3046                 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0);
   3047         ContentValues values2 = new ContentValues();
   3048         final String email2 = "b (at) acme.com";
   3049         createContact(values2, "Sam", "Times", "18004664412",
   3050                 email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0);
   3051         ContentValues values3 = new ContentValues();
   3052         final String phoneNumber3 = "18004664413";
   3053         final long contactId3 = createContact(values3, "Lotta", "Calling", phoneNumber3,
   3054                 "c (at) acme.com", StatusUpdates.AWAY, 0, 1, 0, 0);
   3055         ContentValues values4 = new ContentValues();
   3056         createContact(values4, "Fay", "Veritt", "18004664414",
   3057                 "d (at) acme.com", StatusUpdates.AVAILABLE, 0, 1, 0, 0);
   3058 
   3059         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
   3060 
   3061         assertStoredValues(Contacts.CONTENT_FREQUENT_URI, values1);
   3062 
   3063         // Pretend email was sent to the address twice.
   3064         sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2);
   3065         sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2);
   3066 
   3067         assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values2, values1});
   3068 
   3069         // Three times
   3070         sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
   3071         sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
   3072         sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3);
   3073 
   3074         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
   3075                 new ContentValues[] {values3, values2, values1});
   3076 
   3077         // Test it works with selection/selectionArgs
   3078         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
   3079                 Contacts.STARRED + "=?", new String[] {"0"},
   3080                 new ContentValues[] {values2, values1});
   3081         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
   3082                 Contacts.STARRED + "=?", new String[] {"1"},
   3083                 new ContentValues[] {values3});
   3084 
   3085         values3.put(Contacts.STARRED, 0);
   3086         assertEquals(1,
   3087                 mResolver.update(Uri.withAppendedPath(Contacts.CONTENT_URI,
   3088                         String.valueOf(contactId3)),
   3089                 values3, null, null));
   3090         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
   3091                 Contacts.STARRED + "=?", new String[] {"0"},
   3092                 new ContentValues[] {values3, values2, values1});
   3093         assertStoredValues(Contacts.CONTENT_FREQUENT_URI,
   3094                 Contacts.STARRED + "=?", new String[] {"1"},
   3095                 new ContentValues[] {});
   3096     }
   3097 
   3098     public void testQueryContactFrequentExcludingInvisible() {
   3099         ContentValues values1 = new ContentValues();
   3100         final String email1 = "a (at) acme.com";
   3101         final long cid1 = createContact(values1, "Noah", "Tever", "18004664411",
   3102                 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0);
   3103         ContentValues values2 = new ContentValues();
   3104         final String email2 = "b (at) acme.com";
   3105         final long cid2 = createContact(values2, "Sam", "Times", "18004664412",
   3106                 email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0);
   3107 
   3108         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
   3109         sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2);
   3110 
   3111         // First, we have two contacts in frequent.
   3112         assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values2, values1});
   3113 
   3114         // Contact 2 goes invisible.
   3115         markInvisible(cid2);
   3116 
   3117         // Now we have only 1 frequent.
   3118         assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values1});
   3119 
   3120     }
   3121 
   3122     public void testQueryDataUsageStat() {
   3123         ContentValues values1 = new ContentValues();
   3124         final String email1 = "a (at) acme.com";
   3125         final long cid1 = createContact(values1, "Noah", "Tever", "18004664411",
   3126                 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0);
   3127 
   3128         sMockClock.install();
   3129         sMockClock.setCurrentTimeMillis(100);
   3130 
   3131         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
   3132 
   3133         assertDataUsageCursorContains(Data.CONTENT_URI, "a (at) acme.com", 1, 100);
   3134 
   3135         sMockClock.setCurrentTimeMillis(111);
   3136         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1);
   3137 
   3138         assertDataUsageCursorContains(Data.CONTENT_URI, "a (at) acme.com", 2, 111);
   3139 
   3140         sMockClock.setCurrentTimeMillis(123);
   3141         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1);
   3142 
   3143         assertDataUsageCursorContains(Data.CONTENT_URI, "a (at) acme.com", 3, 123);
   3144 
   3145         final Uri dataUriWithUsageTypeLongText = Data.CONTENT_URI.buildUpon().appendQueryParameter(
   3146                 DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_LONG_TEXT).build();
   3147 
   3148         assertDataUsageCursorContains(dataUriWithUsageTypeLongText, "a (at) acme.com", 2, 111);
   3149 
   3150         sMockClock.setCurrentTimeMillis(200);
   3151         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
   3152         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
   3153         sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1);
   3154 
   3155         assertDataUsageCursorContains(Data.CONTENT_URI, "a (at) acme.com", 6, 200);
   3156 
   3157         final Uri dataUriWithUsageTypeCall = Data.CONTENT_URI.buildUpon().appendQueryParameter(
   3158                 DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_CALL).build();
   3159 
   3160         assertDataUsageCursorContains(dataUriWithUsageTypeCall, "a (at) acme.com", 3, 200);
   3161     }
   3162 
   3163     public void testQueryContactGroup() {
   3164         long groupId = createGroup(null, "testGroup", "Test Group");
   3165 
   3166         ContentValues values1 = new ContentValues();
   3167         createContact(values1, "Best", "West", "18004664411",
   3168                 "west (at) acme.com", StatusUpdates.OFFLINE, 0, 0, groupId,
   3169                 StatusUpdates.CAPABILITY_HAS_CAMERA);
   3170 
   3171         ContentValues values2 = new ContentValues();
   3172         createContact(values2, "Rest", "East", "18004664422",
   3173                 "east (at) acme.com", StatusUpdates.AVAILABLE, 0, 0, 0,
   3174                 StatusUpdates.CAPABILITY_HAS_VOICE);
   3175 
   3176         Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
   3177         Cursor c = mResolver.query(filterUri1, null, null, null, Contacts._ID);
   3178         assertEquals(1, c.getCount());
   3179         c.moveToFirst();
   3180         assertCursorValues(c, values1);
   3181         c.close();
   3182 
   3183         Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group");
   3184         c = mResolver.query(filterUri2, null, Contacts.DISPLAY_NAME + "=?",
   3185                 new String[] { "Best West" }, Contacts._ID);
   3186         assertEquals(1, c.getCount());
   3187         c.close();
   3188 
   3189         Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Next Group");
   3190         c = mResolver.query(filterUri3, null, null, null, Contacts._ID);
   3191         assertEquals(0, c.getCount());
   3192         c.close();
   3193     }
   3194 
   3195     private void expectSecurityException(String failureMessage, Uri uri, String[] projection,
   3196             String selection, String[] selectionArgs, String sortOrder) {
   3197         Cursor c = null;
   3198         try {
   3199             c = mResolver.query(uri, projection, selection, selectionArgs, sortOrder);
   3200             fail(failureMessage);
   3201         } catch (SecurityException expected) {
   3202             // The security exception is expected to occur because we're missing a permission.
   3203         } finally {
   3204             if (c != null) {
   3205                 c.close();
   3206             }
   3207         }
   3208     }
   3209 
   3210     public void testQueryProfileRequiresReadPermission() {
   3211         mActor.removePermissions("android.permission.READ_PROFILE");
   3212 
   3213         createBasicProfileContact(new ContentValues());
   3214 
   3215         // Case 1: Retrieving profile contact.
   3216         expectSecurityException(
   3217                 "Querying for the profile without READ_PROFILE access should fail.",
   3218                 Profile.CONTENT_URI, null, null, null, Contacts._ID);
   3219 
   3220         // Case 2: Retrieving profile data.
   3221         expectSecurityException(
   3222                 "Querying for the profile data without READ_PROFILE access should fail.",
   3223                 Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
   3224                 null, null, null, Contacts._ID);
   3225 
   3226         // Case 3: Retrieving profile entities.
   3227         expectSecurityException(
   3228                 "Querying for the profile entities without READ_PROFILE access should fail.",
   3229                 Profile.CONTENT_URI.buildUpon()
   3230                         .appendPath("entities").build(), null, null, null, Contacts._ID);
   3231     }
   3232 
   3233     public void testQueryProfileByContactIdRequiresReadPermission() {
   3234         long profileRawContactId = createBasicProfileContact(new ContentValues());
   3235         long profileContactId = queryContactId(profileRawContactId);
   3236 
   3237         mActor.removePermissions("android.permission.READ_PROFILE");
   3238 
   3239         // A query for the profile contact by ID should fail.
   3240         expectSecurityException(
   3241                 "Querying for the profile by contact ID without READ_PROFILE access should fail.",
   3242                 ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId),
   3243                 null, null, null, Contacts._ID);
   3244     }
   3245 
   3246     public void testQueryProfileByRawContactIdRequiresReadPermission() {
   3247         long profileRawContactId = createBasicProfileContact(new ContentValues());
   3248 
   3249         // Remove profile read permission and attempt to retrieve the raw contact.
   3250         mActor.removePermissions("android.permission.READ_PROFILE");
   3251         expectSecurityException(
   3252                 "Querying for the raw contact profile without READ_PROFILE access should fail.",
   3253                 ContentUris.withAppendedId(RawContacts.CONTENT_URI,
   3254                         profileRawContactId), null, null, null, RawContacts._ID);
   3255     }
   3256 
   3257     public void testQueryProfileRawContactRequiresReadPermission() {
   3258         long profileRawContactId = createBasicProfileContact(new ContentValues());
   3259 
   3260         // Remove profile read permission and attempt to retrieve the profile's raw contact data.
   3261         mActor.removePermissions("android.permission.READ_PROFILE");
   3262 
   3263         // Case 1: Retrieve the overall raw contact set for the profile.
   3264         expectSecurityException(
   3265                 "Querying for the raw contact profile without READ_PROFILE access should fail.",
   3266                 Profile.CONTENT_RAW_CONTACTS_URI, null, null, null, null);
   3267 
   3268         // Case 2: Retrieve the raw contact profile data for the inserted raw contact ID.
   3269         expectSecurityException(
   3270                 "Querying for the raw profile data without READ_PROFILE access should fail.",
   3271                 ContentUris.withAppendedId(
   3272                         Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
   3273                         .appendPath("data").build(), null, null, null, null);
   3274 
   3275         // Case 3: Retrieve the raw contact profile entity for the inserted raw contact ID.
   3276         expectSecurityException(
   3277                 "Querying for the raw profile entities without READ_PROFILE access should fail.",
   3278                 ContentUris.withAppendedId(
   3279                         Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
   3280                         .appendPath("entity").build(), null, null, null, null);
   3281     }
   3282 
   3283     public void testQueryProfileDataByDataIdRequiresReadPermission() {
   3284         createBasicProfileContact(new ContentValues());
   3285         Cursor c = mResolver.query(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
   3286                 new String[]{Data._ID, Data.MIMETYPE}, null, null, null);
   3287         assertEquals(4, c.getCount());  // Photo, phone, email, name.
   3288         c.moveToFirst();
   3289         long profileDataId = c.getLong(0);
   3290         c.close();
   3291 
   3292         // Remove profile read permission and attempt to retrieve the data
   3293         mActor.removePermissions("android.permission.READ_PROFILE");
   3294         expectSecurityException(
   3295                 "Querying for the data in the profile without READ_PROFILE access should fail.",
   3296                 ContentUris.withAppendedId(Data.CONTENT_URI, profileDataId),
   3297                 null, null, null, null);
   3298     }
   3299 
   3300     public void testQueryProfileDataRequiresReadPermission() {
   3301         createBasicProfileContact(new ContentValues());
   3302 
   3303         // Remove profile read permission and attempt to retrieve all profile data.
   3304         mActor.removePermissions("android.permission.READ_PROFILE");
   3305         expectSecurityException(
   3306                 "Querying for the data in the profile without READ_PROFILE access should fail.",
   3307                 Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
   3308                 null, null, null, null);
   3309     }
   3310 
   3311     public void testInsertProfileRequiresWritePermission() {
   3312         mActor.removePermissions("android.permission.WRITE_PROFILE");
   3313 
   3314         // Creating a non-profile contact should be fine.
   3315         createBasicNonProfileContact(new ContentValues());
   3316 
   3317         // Creating a profile contact should throw an exception.
   3318         try {
   3319             createBasicProfileContact(new ContentValues());
   3320             fail("Creating a profile contact should fail without WRITE_PROFILE access.");
   3321         } catch (SecurityException expected) {
   3322         }
   3323     }
   3324 
   3325     public void testInsertProfileDataRequiresWritePermission() {
   3326         long profileRawContactId = createBasicProfileContact(new ContentValues());
   3327 
   3328         mActor.removePermissions("android.permission.WRITE_PROFILE");
   3329         try {
   3330             insertEmail(profileRawContactId, "foo (at) bar.net", false);
   3331             fail("Inserting data into a profile contact should fail without WRITE_PROFILE access.");
   3332         } catch (SecurityException expected) {
   3333         }
   3334     }
   3335 
   3336     public void testUpdateDataDoesNotRequireProfilePermission() {
   3337         mActor.removePermissions("android.permission.READ_PROFILE");
   3338         mActor.removePermissions("android.permission.WRITE_PROFILE");
   3339 
   3340         // Create a non-profile contact.
   3341         long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Domo", "Arigato");
   3342         long dataId = getStoredLongValue(Data.CONTENT_URI,
   3343                 Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
   3344                 new String[]{String.valueOf(rawContactId), StructuredName.CONTENT_ITEM_TYPE},
   3345                 Data._ID);
   3346 
   3347         // Updates its name using a selection.
   3348         ContentValues values = new ContentValues();
   3349         values.put(StructuredName.GIVEN_NAME, "Bob");
   3350         values.put(StructuredName.FAMILY_NAME, "Blob");
   3351         mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?",
   3352                 new String[]{String.valueOf(dataId)});
   3353 
   3354         // Check that the update went through.
   3355         assertStoredValues(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), values);
   3356     }
   3357 
   3358     public void testQueryContactThenProfile() {
   3359         ContentValues profileValues = new ContentValues();
   3360         long profileRawContactId = createBasicProfileContact(profileValues);
   3361         long profileContactId = queryContactId(profileRawContactId);
   3362 
   3363         ContentValues nonProfileValues = new ContentValues();
   3364         long nonProfileRawContactId = createBasicNonProfileContact(nonProfileValues);
   3365         long nonProfileContactId = queryContactId(nonProfileRawContactId);
   3366 
   3367         assertStoredValues(Contacts.CONTENT_URI, nonProfileValues);
   3368         assertSelection(Contacts.CONTENT_URI, nonProfileValues, Contacts._ID, nonProfileContactId);
   3369 
   3370         assertStoredValues(Profile.CONTENT_URI, profileValues);
   3371     }
   3372 
   3373     public void testQueryContactExcludeProfile() {
   3374         // Create a profile contact (it should not be returned by the general contact URI).
   3375         createBasicProfileContact(new ContentValues());
   3376 
   3377         // Create a non-profile contact - this should be returned.
   3378         ContentValues nonProfileValues = new ContentValues();
   3379         createBasicNonProfileContact(nonProfileValues);
   3380 
   3381         assertStoredValues(Contacts.CONTENT_URI, new ContentValues[] {nonProfileValues});
   3382     }
   3383 
   3384     public void testQueryProfile() {
   3385         ContentValues profileValues = new ContentValues();
   3386         createBasicProfileContact(profileValues);
   3387 
   3388         assertStoredValues(Profile.CONTENT_URI, profileValues);
   3389     }
   3390 
   3391     private ContentValues[] getExpectedProfileDataValues() {
   3392         // Expected photo data values (only field is the photo BLOB, which we can't check).
   3393         ContentValues photoRow = new ContentValues();
   3394         photoRow.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
   3395 
   3396         // Expected phone data values.
   3397         ContentValues phoneRow = new ContentValues();
   3398         phoneRow.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   3399         phoneRow.put(Phone.NUMBER, "18005554411");
   3400 
   3401         // Expected email data values.
   3402         ContentValues emailRow = new ContentValues();
   3403         emailRow.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   3404         emailRow.put(Email.ADDRESS, "mia.prophyl (at) acme.com");
   3405 
   3406         // Expected name data values.
   3407         ContentValues nameRow = new ContentValues();
   3408         nameRow.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
   3409         nameRow.put(StructuredName.DISPLAY_NAME, "Mia Prophyl");
   3410         nameRow.put(StructuredName.GIVEN_NAME, "Mia");
   3411         nameRow.put(StructuredName.FAMILY_NAME, "Prophyl");
   3412 
   3413         return new ContentValues[]{photoRow, phoneRow, emailRow, nameRow};
   3414     }
   3415 
   3416     public void testQueryProfileData() {
   3417         createBasicProfileContact(new ContentValues());
   3418 
   3419         assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
   3420                 getExpectedProfileDataValues());
   3421     }
   3422 
   3423     public void testQueryProfileEntities() {
   3424         createBasicProfileContact(new ContentValues());
   3425 
   3426         assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("entities").build(),
   3427                 getExpectedProfileDataValues());
   3428     }
   3429 
   3430     public void testQueryRawProfile() {
   3431         ContentValues profileValues = new ContentValues();
   3432         createBasicProfileContact(profileValues);
   3433 
   3434         // The raw contact view doesn't include the photo ID.
   3435         profileValues.remove(Contacts.PHOTO_ID);
   3436         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, profileValues);
   3437     }
   3438 
   3439     public void testQueryRawProfileById() {
   3440         ContentValues profileValues = new ContentValues();
   3441         long profileRawContactId = createBasicProfileContact(profileValues);
   3442 
   3443         // The raw contact view doesn't include the photo ID.
   3444         profileValues.remove(Contacts.PHOTO_ID);
   3445         assertStoredValues(ContentUris.withAppendedId(
   3446                 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId), profileValues);
   3447     }
   3448 
   3449     public void testQueryRawProfileData() {
   3450         long profileRawContactId = createBasicProfileContact(new ContentValues());
   3451 
   3452         assertStoredValues(ContentUris.withAppendedId(
   3453                 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
   3454                 .appendPath("data").build(), getExpectedProfileDataValues());
   3455     }
   3456 
   3457     public void testQueryRawProfileEntity() {
   3458         long profileRawContactId = createBasicProfileContact(new ContentValues());
   3459 
   3460         assertStoredValues(ContentUris.withAppendedId(
   3461                 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon()
   3462                 .appendPath("entity").build(), getExpectedProfileDataValues());
   3463     }
   3464 
   3465     public void testQueryDataForProfile() {
   3466         createBasicProfileContact(new ContentValues());
   3467 
   3468         assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(),
   3469                 getExpectedProfileDataValues());
   3470     }
   3471 
   3472     public void testUpdateProfileRawContact() {
   3473         createBasicProfileContact(new ContentValues());
   3474         ContentValues updatedValues = new ContentValues();
   3475         updatedValues.put(RawContacts.SEND_TO_VOICEMAIL, 0);
   3476         updatedValues.put(RawContacts.CUSTOM_RINGTONE, "rachmaninoff3");
   3477         updatedValues.put(RawContacts.STARRED, 1);
   3478         mResolver.update(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues, null, null);
   3479 
   3480         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues);
   3481     }
   3482 
   3483     public void testInsertProfileWithDataSetTriggersAccountCreation() {
   3484         // Check that we have no profile raw contacts.
   3485         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, new ContentValues[]{});
   3486 
   3487         // Insert a profile record with a new data set.
   3488         Account account = new Account("a", "b");
   3489         String dataSet = "c";
   3490         Uri profileUri = TestUtil.maybeAddAccountQueryParameters(Profile.CONTENT_RAW_CONTACTS_URI,
   3491                 account)
   3492                 .buildUpon().appendQueryParameter(RawContacts.DATA_SET, dataSet).build();
   3493         ContentValues values = new ContentValues();
   3494         long rawContactId = ContentUris.parseId(mResolver.insert(profileUri, values));
   3495         values.put(RawContacts._ID, rawContactId);
   3496 
   3497         // Check that querying for the profile gets the created raw contact.
   3498         assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, values);
   3499     }
   3500 
   3501     public void testLoadProfilePhoto() throws IOException {
   3502         long rawContactId = createBasicProfileContact(new ContentValues());
   3503         insertPhoto(rawContactId, R.drawable.earth_normal);
   3504         EvenMoreAsserts.assertImageRawData(getContext(),
   3505                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.THUMBNAIL),
   3506                 Contacts.openContactPhotoInputStream(mResolver, Profile.CONTENT_URI, false));
   3507     }
   3508 
   3509     public void testLoadProfileDisplayPhoto() throws IOException {
   3510         long rawContactId = createBasicProfileContact(new ContentValues());
   3511         insertPhoto(rawContactId, R.drawable.earth_normal);
   3512         EvenMoreAsserts.assertImageRawData(getContext(),
   3513                 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO),
   3514                 Contacts.openContactPhotoInputStream(mResolver,