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.ContactsActor.PACKAGE_GREY;
     20 import static com.android.providers.contacts.TestUtils.cv;
     21 import static com.android.providers.contacts.TestUtils.dumpCursor;
     22 
     23 import android.accounts.Account;
     24 import android.content.ContentProvider;
     25 import android.content.ContentResolver;
     26 import android.content.ContentUris;
     27 import android.content.ContentValues;
     28 import android.content.Context;
     29 import android.content.Entity;
     30 import android.database.Cursor;
     31 import android.database.sqlite.SQLiteDatabase;
     32 import android.net.Uri;
     33 import android.provider.BaseColumns;
     34 import android.provider.CallLog;
     35 import android.provider.CallLog.Calls;
     36 import android.provider.ContactsContract;
     37 import android.provider.ContactsContract.AggregationExceptions;
     38 import android.provider.ContactsContract.CommonDataKinds.Email;
     39 import android.provider.ContactsContract.CommonDataKinds.Event;
     40 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
     41 import android.provider.ContactsContract.CommonDataKinds.Identity;
     42 import android.provider.ContactsContract.CommonDataKinds.Im;
     43 import android.provider.ContactsContract.CommonDataKinds.Nickname;
     44 import android.provider.ContactsContract.CommonDataKinds.Note;
     45 import android.provider.ContactsContract.CommonDataKinds.Organization;
     46 import android.provider.ContactsContract.CommonDataKinds.Phone;
     47 import android.provider.ContactsContract.CommonDataKinds.Photo;
     48 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
     49 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
     50 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
     51 import android.provider.ContactsContract.Contacts;
     52 import android.provider.ContactsContract.Data;
     53 import android.provider.ContactsContract.Groups;
     54 import android.provider.ContactsContract.RawContacts;
     55 import android.provider.ContactsContract.Settings;
     56 import android.provider.ContactsContract.StatusUpdates;
     57 import android.provider.ContactsContract.StreamItems;
     58 import android.provider.VoicemailContract;
     59 import android.test.MoreAsserts;
     60 import android.test.mock.MockContentResolver;
     61 import android.util.Log;
     62 import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
     63 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
     64 import com.android.providers.contacts.testutil.CommonDatabaseUtils;
     65 import com.android.providers.contacts.testutil.DataUtil;
     66 import com.android.providers.contacts.testutil.RawContactUtil;
     67 import com.android.providers.contacts.testutil.TestUtil;
     68 import com.android.providers.contacts.util.Hex;
     69 import com.android.providers.contacts.util.MockClock;
     70 import com.google.android.collect.Sets;
     71 
     72 import java.util.ArrayList;
     73 import java.util.Arrays;
     74 import java.util.BitSet;
     75 import java.util.Comparator;
     76 import java.util.HashSet;
     77 import java.util.Iterator;
     78 import java.util.Map;
     79 import java.util.Map.Entry;
     80 import java.util.Set;
     81 
     82 /**
     83  * A common superclass for {@link ContactsProvider2}-related tests.
     84  */
     85 public abstract class BaseContactsProvider2Test extends PhotoLoadingTestCase {
     86 
     87     static final String ADD_VOICEMAIL_PERMISSION =
     88             "com.android.voicemail.permission.ADD_VOICEMAIL";
     89     /*
     90      * Permission to allow querying voicemails
     91      */
     92     static final String READ_VOICEMAIL_PERMISSION =
     93             "com.android.voicemail.permission.READ_VOICEMAIL";
     94     /*
     95      * Permission to allow deleting and updating voicemails
     96      */
     97     static final String WRITE_VOICEMAIL_PERMISSION =
     98             "com.android.voicemail.permission.WRITE_VOICEMAIL";
     99 
    100     protected static final String PACKAGE = "ContactsProvider2Test";
    101     public static final String READ_ONLY_ACCOUNT_TYPE =
    102             SynchronousContactsProvider2.READ_ONLY_ACCOUNT_TYPE;
    103 
    104     protected ContactsActor mActor;
    105     protected MockContentResolver mResolver;
    106     protected Account mAccount = new Account("account1", "account type1");
    107     protected Account mAccountTwo = new Account("account2", "account type2");
    108 
    109     protected final static Long NO_LONG = new Long(0);
    110     protected final static String NO_STRING = new String("");
    111     protected final static Account NO_ACCOUNT = new Account("a", "b");
    112 
    113     /**
    114      * Use {@link MockClock#install()} to start using it.
    115      * It'll be automatically uninstalled by {@link #tearDown()}.
    116      */
    117     protected static final MockClock sMockClock = new MockClock();
    118 
    119     protected Class<? extends ContentProvider> getProviderClass() {
    120         return SynchronousContactsProvider2.class;
    121     }
    122 
    123     protected String getAuthority() {
    124         return ContactsContract.AUTHORITY;
    125     }
    126 
    127     @Override
    128     protected void setUp() throws Exception {
    129         super.setUp();
    130 
    131         mActor = new ContactsActor(
    132                 getContext(), getContextPackageName(), getProviderClass(), getAuthority());
    133         mResolver = mActor.resolver;
    134         if (mActor.provider instanceof SynchronousContactsProvider2) {
    135             getContactsProvider().wipeData();
    136         }
    137 
    138         // Give the actor access to read/write contacts and profile data by default.
    139         mActor.addPermissions(
    140                 "android.permission.READ_CONTACTS",
    141                 "android.permission.WRITE_CONTACTS",
    142                 "android.permission.READ_WRITE_CONTACT_METADATA",
    143                 "android.permission.READ_SOCIAL_STREAM",
    144                 "android.permission.WRITE_SOCIAL_STREAM");
    145     }
    146 
    147     protected String getContextPackageName() {
    148         return PACKAGE_GREY;
    149     }
    150 
    151     @Override
    152     protected void tearDown() throws Exception {
    153         mActor.shutdown();
    154         sMockClock.uninstall();
    155         super.tearDown();
    156     }
    157 
    158     public SynchronousContactsProvider2 getContactsProvider() {
    159         return (SynchronousContactsProvider2) mActor.provider;
    160     }
    161 
    162     public Context getMockContext() {
    163         return mActor.context;
    164     }
    165 
    166     public <T extends ContentProvider> T addProvider(Class<T> providerClass,
    167             String authority) throws Exception {
    168         return mActor.addProvider(providerClass, authority);
    169     }
    170 
    171     public ContentProvider getProvider() {
    172         return mActor.provider;
    173     }
    174 
    175     protected Uri setCallerIsSyncAdapter(Uri uri, Account account) {
    176         if (account == null) {
    177             return uri;
    178         }
    179         final Uri.Builder builder = uri.buildUpon();
    180         builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, account.name);
    181         builder.appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type);
    182         builder.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true");
    183         return builder.build();
    184     }
    185 
    186     protected int updateItem(Uri uri, long id, String... extras) {
    187         Uri itemUri = ContentUris.withAppendedId(uri, id);
    188         return updateItem(itemUri, extras);
    189     }
    190 
    191     protected int updateItem(Uri uri, String... extras) {
    192         ContentValues values = new ContentValues();
    193         CommonDatabaseUtils.extrasVarArgsToValues(values, extras);
    194         return mResolver.update(uri, values, null, null);
    195     }
    196 
    197     protected long createGroup(Account account, String sourceId, String title) {
    198         return createGroup(account, sourceId, title, 1, false, false);
    199     }
    200 
    201     protected long createGroup(Account account, String sourceId, String title, int visible) {
    202         return createGroup(account, sourceId, title, visible, false, false);
    203     }
    204 
    205     protected long createAutoAddGroup(Account account) {
    206         return createGroup(account, "auto", "auto",
    207                 0 /* visible */,  true /* auto-add */, false /* fav */);
    208     }
    209 
    210     protected long createGroup(Account account, String sourceId, String title,
    211             int visible, boolean autoAdd, boolean favorite) {
    212         ContentValues values = new ContentValues();
    213         values.put(Groups.SOURCE_ID, sourceId);
    214         values.put(Groups.TITLE, title);
    215         values.put(Groups.GROUP_VISIBLE, visible);
    216         values.put(Groups.AUTO_ADD, autoAdd ? 1 : 0);
    217         values.put(Groups.FAVORITES, favorite ? 1 : 0);
    218         final Uri uri = TestUtil.maybeAddAccountQueryParameters(Groups.CONTENT_URI, account);
    219         return ContentUris.parseId(mResolver.insert(uri, values));
    220     }
    221 
    222     protected void createSettings(Account account, String shouldSync, String ungroupedVisible) {
    223         createSettings(new AccountWithDataSet(account.name, account.type, null),
    224                 shouldSync, ungroupedVisible);
    225     }
    226 
    227     protected void createSettings(AccountWithDataSet account, String shouldSync,
    228             String ungroupedVisible) {
    229         ContentValues values = new ContentValues();
    230         values.put(Settings.ACCOUNT_NAME, account.getAccountName());
    231         values.put(Settings.ACCOUNT_TYPE, account.getAccountType());
    232         if (account.getDataSet() != null) {
    233             values.put(Settings.DATA_SET, account.getDataSet());
    234         }
    235         values.put(Settings.SHOULD_SYNC, shouldSync);
    236         values.put(Settings.UNGROUPED_VISIBLE, ungroupedVisible);
    237         mResolver.insert(Settings.CONTENT_URI, values);
    238     }
    239 
    240     protected Uri insertOrganization(long rawContactId, ContentValues values) {
    241         return insertOrganization(rawContactId, values, false, false);
    242     }
    243 
    244     protected Uri insertOrganization(long rawContactId, ContentValues values, boolean primary) {
    245         return insertOrganization(rawContactId, values, primary, false);
    246     }
    247 
    248     protected Uri insertOrganization(long rawContactId, ContentValues values, boolean primary,
    249             boolean superPrimary) {
    250         values.put(Data.RAW_CONTACT_ID, rawContactId);
    251         values.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
    252         values.put(Organization.TYPE, Organization.TYPE_WORK);
    253         if (primary) {
    254             values.put(Data.IS_PRIMARY, 1);
    255         }
    256         if (superPrimary) {
    257             values.put(Data.IS_SUPER_PRIMARY, 1);
    258         }
    259 
    260         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    261         return resultUri;
    262     }
    263 
    264     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber) {
    265         return insertPhoneNumber(rawContactId, phoneNumber, false);
    266     }
    267 
    268     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary) {
    269         return insertPhoneNumber(rawContactId, phoneNumber, primary, false, Phone.TYPE_HOME);
    270     }
    271 
    272     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary,
    273             boolean superPrimary) {
    274         return insertPhoneNumber(rawContactId, phoneNumber, primary, superPrimary, Phone.TYPE_HOME);
    275     }
    276 
    277     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary,
    278             int type) {
    279         return insertPhoneNumber(rawContactId, phoneNumber, primary, false, type);
    280     }
    281 
    282     protected Uri insertPhoneNumber(long rawContactId, String phoneNumber, boolean primary,
    283             boolean superPrimary, int type) {
    284         ContentValues values = new ContentValues();
    285         values.put(Data.RAW_CONTACT_ID, rawContactId);
    286         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    287         values.put(Phone.NUMBER, phoneNumber);
    288         values.put(Phone.TYPE, type);
    289         if (primary) {
    290             values.put(Data.IS_PRIMARY, 1);
    291         }
    292         if (superPrimary) {
    293             values.put(Data.IS_SUPER_PRIMARY, 1);
    294         }
    295 
    296         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    297         return resultUri;
    298     }
    299 
    300     protected Uri insertEmail(long rawContactId, String email) {
    301         return insertEmail(rawContactId, email, false);
    302     }
    303 
    304     protected Uri insertEmail(long rawContactId, String email, boolean primary) {
    305         return insertEmail(rawContactId, email, primary, Email.TYPE_HOME, null);
    306     }
    307 
    308     protected Uri insertEmail(long rawContactId, String email, boolean primary,
    309             boolean superPrimary) {
    310         return insertEmail(rawContactId, email, primary, superPrimary, Email.TYPE_HOME, null);
    311     }
    312 
    313     protected Uri insertEmail(long rawContactId, String email, boolean primary, int type,
    314             String label) {
    315         return insertEmail(rawContactId, email, primary, false, type, label);
    316     }
    317 
    318     protected Uri insertEmail(long rawContactId, String email, boolean primary,
    319             boolean superPrimary, int type,  String label) {
    320         ContentValues values = new ContentValues();
    321         values.put(Data.RAW_CONTACT_ID, rawContactId);
    322         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
    323         values.put(Email.DATA, email);
    324         values.put(Email.TYPE, type);
    325         values.put(Email.LABEL, label);
    326         if (primary) {
    327             values.put(Data.IS_PRIMARY, 1);
    328         }
    329         if (superPrimary) {
    330             values.put(Data.IS_SUPER_PRIMARY, 1);
    331         }
    332 
    333         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    334         return resultUri;
    335     }
    336 
    337     protected Uri insertSipAddress(long rawContactId, String sipAddress) {
    338         return insertSipAddress(rawContactId, sipAddress, false);
    339     }
    340 
    341     protected Uri insertSipAddress(long rawContactId, String sipAddress, boolean primary) {
    342         ContentValues values = new ContentValues();
    343         values.put(Data.RAW_CONTACT_ID, rawContactId);
    344         values.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
    345         values.put(SipAddress.SIP_ADDRESS, sipAddress);
    346         if (primary) {
    347             values.put(Data.IS_PRIMARY, 1);
    348         }
    349 
    350         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    351         return resultUri;
    352     }
    353 
    354     protected Uri insertNickname(long rawContactId, String nickname) {
    355         ContentValues values = new ContentValues();
    356         values.put(Data.RAW_CONTACT_ID, rawContactId);
    357         values.put(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
    358         values.put(Nickname.NAME, nickname);
    359         values.put(Nickname.TYPE, Nickname.TYPE_OTHER_NAME);
    360 
    361         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    362         return resultUri;
    363     }
    364 
    365     protected Uri insertPostalAddress(long rawContactId, String formattedAddress) {
    366         ContentValues values = new ContentValues();
    367         values.put(Data.RAW_CONTACT_ID, rawContactId);
    368         values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
    369         values.put(StructuredPostal.FORMATTED_ADDRESS, formattedAddress);
    370 
    371         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    372         return resultUri;
    373     }
    374 
    375     protected Uri insertPostalAddress(long rawContactId, ContentValues values) {
    376         values.put(Data.RAW_CONTACT_ID, rawContactId);
    377         values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
    378         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    379         return resultUri;
    380     }
    381 
    382     protected Uri insertPhoto(long rawContactId) {
    383         ContentValues values = new ContentValues();
    384         values.put(Data.RAW_CONTACT_ID, rawContactId);
    385         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
    386         values.put(Photo.PHOTO, loadTestPhoto());
    387         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    388         return resultUri;
    389     }
    390 
    391     protected Uri insertPhoto(long rawContactId, int resourceId) {
    392         ContentValues values = new ContentValues();
    393         values.put(Data.RAW_CONTACT_ID, rawContactId);
    394         values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
    395         values.put(Photo.PHOTO, loadPhotoFromResource(resourceId, PhotoSize.ORIGINAL));
    396         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    397         return resultUri;
    398     }
    399 
    400     protected Uri insertGroupMembership(long rawContactId, String sourceId) {
    401         ContentValues values = new ContentValues();
    402         values.put(Data.RAW_CONTACT_ID, rawContactId);
    403         values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
    404         values.put(GroupMembership.GROUP_SOURCE_ID, sourceId);
    405         return mResolver.insert(Data.CONTENT_URI, values);
    406     }
    407 
    408     protected Uri insertGroupMembership(long rawContactId, Long groupId) {
    409         ContentValues values = new ContentValues();
    410         values.put(Data.RAW_CONTACT_ID, rawContactId);
    411         values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
    412         values.put(GroupMembership.GROUP_ROW_ID, groupId);
    413         return mResolver.insert(Data.CONTENT_URI, values);
    414     }
    415 
    416     public void removeGroupMemberships(long rawContactId) {
    417         mResolver.delete(Data.CONTENT_URI,
    418                 Data.MIMETYPE + "=? AND " + GroupMembership.RAW_CONTACT_ID + "=?",
    419                 new String[] { GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(rawContactId) });
    420     }
    421 
    422     protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle,
    423             int presence, String status, int chatMode) {
    424         return insertStatusUpdate(protocol, customProtocol, handle, presence, status, chatMode,
    425                 false);
    426     }
    427 
    428     protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle,
    429             int presence, String status, int chatMode, boolean isUserProfile) {
    430         return insertStatusUpdate(protocol, customProtocol, handle, presence, status, 0, chatMode,
    431                 isUserProfile);
    432     }
    433 
    434     protected Uri insertStatusUpdate(int protocol, String customProtocol, String handle,
    435             int presence, String status, long timestamp, int chatMode, boolean isUserProfile) {
    436         ContentValues values = new ContentValues();
    437         values.put(StatusUpdates.PROTOCOL, protocol);
    438         values.put(StatusUpdates.CUSTOM_PROTOCOL, customProtocol);
    439         values.put(StatusUpdates.IM_HANDLE, handle);
    440         return insertStatusUpdate(values, presence, status, timestamp, chatMode, isUserProfile);
    441     }
    442 
    443     protected Uri insertStatusUpdate(
    444             long dataId, int presence, String status, long timestamp, int chatMode) {
    445         return insertStatusUpdate(dataId, presence, status, timestamp, chatMode, false);
    446     }
    447 
    448     protected Uri insertStatusUpdate(
    449             long dataId, int presence, String status, long timestamp, int chatMode,
    450             boolean isUserProfile) {
    451         ContentValues values = new ContentValues();
    452         values.put(StatusUpdates.DATA_ID, dataId);
    453         return insertStatusUpdate(values, presence, status, timestamp, chatMode, isUserProfile);
    454     }
    455 
    456     private Uri insertStatusUpdate(
    457             ContentValues values, int presence, String status, long timestamp, int chatMode,
    458             boolean isUserProfile) {
    459         if (presence != 0) {
    460             values.put(StatusUpdates.PRESENCE, presence);
    461             values.put(StatusUpdates.CHAT_CAPABILITY, chatMode);
    462         }
    463         if (status != null) {
    464             values.put(StatusUpdates.STATUS, status);
    465         }
    466         if (timestamp != 0) {
    467             values.put(StatusUpdates.STATUS_TIMESTAMP, timestamp);
    468         }
    469 
    470         Uri insertUri = isUserProfile
    471                 ? StatusUpdates.PROFILE_CONTENT_URI
    472                 : StatusUpdates.CONTENT_URI;
    473         Uri resultUri = mResolver.insert(insertUri, values);
    474         return resultUri;
    475     }
    476 
    477     protected Uri insertStreamItem(long rawContactId, ContentValues values, Account account) {
    478         return mResolver.insert(
    479                 TestUtil.maybeAddAccountQueryParameters(Uri.withAppendedPath(
    480                         ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
    481                         RawContacts.StreamItems.CONTENT_DIRECTORY), account),
    482                 values);
    483     }
    484 
    485     protected Uri insertStreamItemPhoto(long streamItemId, ContentValues values, Account account) {
    486         return mResolver.insert(
    487                 TestUtil.maybeAddAccountQueryParameters(Uri.withAppendedPath(
    488                         ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
    489                         StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), account),
    490                 values);
    491     }
    492 
    493     protected Uri insertImHandle(long rawContactId, int protocol, String customProtocol,
    494             String handle) {
    495         ContentValues values = new ContentValues();
    496         values.put(Data.RAW_CONTACT_ID, rawContactId);
    497         values.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
    498         values.put(Im.PROTOCOL, protocol);
    499         values.put(Im.CUSTOM_PROTOCOL, customProtocol);
    500         values.put(Im.DATA, handle);
    501         values.put(Im.TYPE, Im.TYPE_HOME);
    502 
    503         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    504         return resultUri;
    505     }
    506 
    507     protected Uri insertEvent(long rawContactId, int type, String date) {
    508         ContentValues values = new ContentValues();
    509         values.put(Data.RAW_CONTACT_ID, rawContactId);
    510         values.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
    511         values.put(Event.TYPE, type);
    512         values.put(Event.START_DATE, date);
    513         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    514         return resultUri;
    515     }
    516 
    517     protected Uri insertNote(long rawContactId, String note) {
    518         ContentValues values = new ContentValues();
    519         values.put(Data.RAW_CONTACT_ID, rawContactId);
    520         values.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
    521         values.put(Note.NOTE, note);
    522         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    523         return resultUri;
    524     }
    525 
    526     protected Uri insertIdentity(long rawContactId, String identity, String namespace) {
    527         ContentValues values = new ContentValues();
    528         values.put(Data.RAW_CONTACT_ID, rawContactId);
    529         values.put(Data.MIMETYPE, Identity.CONTENT_ITEM_TYPE);
    530         values.put(Identity.NAMESPACE, namespace);
    531         values.put(Identity.IDENTITY, identity);
    532 
    533         Uri resultUri = mResolver.insert(Data.CONTENT_URI, values);
    534         return resultUri;
    535     }
    536 
    537     protected void setContactAccount(long rawContactId, String accountType, String accountName) {
    538         ContentValues values = new ContentValues();
    539         values.put(RawContacts.ACCOUNT_TYPE, accountType);
    540         values.put(RawContacts.ACCOUNT_NAME, accountName);
    541 
    542         mResolver.update(ContentUris.withAppendedId(
    543                 RawContacts.CONTENT_URI, rawContactId), values, null, null);
    544     }
    545 
    546     protected void setAggregationException(int type, long rawContactId1, long rawContactId2) {
    547         ContentValues values = new ContentValues();
    548         values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
    549         values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
    550         values.put(AggregationExceptions.TYPE, type);
    551         assertEquals(1, mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null));
    552     }
    553 
    554     protected void setRawContactCustomization(long rawContactId, int starred, int sendToVoiceMail) {
    555         ContentValues values = new ContentValues();
    556 
    557         values.put(RawContacts.STARRED, starred);
    558         values.put(RawContacts.SEND_TO_VOICEMAIL, sendToVoiceMail);
    559 
    560         assertEquals(1, mResolver.update(ContentUris.withAppendedId(
    561                 RawContacts.CONTENT_URI, rawContactId), values, null, null));
    562     }
    563 
    564     protected void markInvisible(long contactId) {
    565         // There's no api for this, so we just tweak the DB directly.
    566         SQLiteDatabase db = ((ContactsProvider2) getProvider()).getDatabaseHelper()
    567                 .getWritableDatabase();
    568         db.execSQL("DELETE FROM " + Tables.DEFAULT_DIRECTORY +
    569                 " WHERE " + BaseColumns._ID + "=" + contactId);
    570     }
    571 
    572     protected long createAccount(String accountName, String accountType, String dataSet) {
    573         // There's no api for this, so we just tweak the DB directly.
    574         SQLiteDatabase db = ((ContactsProvider2) getProvider()).getDatabaseHelper()
    575                 .getWritableDatabase();
    576 
    577         ContentValues values = new ContentValues();
    578         values.put(AccountsColumns.ACCOUNT_NAME, accountName);
    579         values.put(AccountsColumns.ACCOUNT_TYPE, accountType);
    580         values.put(AccountsColumns.DATA_SET, dataSet);
    581         return db.insert(Tables.ACCOUNTS, null, values);
    582     }
    583 
    584     protected Cursor queryRawContact(long rawContactId) {
    585         return mResolver.query(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
    586                 null, null, null, null);
    587     }
    588 
    589     protected Cursor queryContact(long contactId) {
    590         return mResolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
    591                 null, null, null, null);
    592     }
    593 
    594     protected Cursor queryContact(long contactId, String[] projection) {
    595         return mResolver.query(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
    596                 projection, null, null, null);
    597     }
    598 
    599     protected Uri getContactUriForRawContact(long rawContactId) {
    600         return ContentUris.withAppendedId(Contacts.CONTENT_URI, queryContactId(rawContactId));
    601     }
    602 
    603     protected long queryContactId(long rawContactId) {
    604         Cursor c = queryRawContact(rawContactId);
    605         assertTrue(c.moveToFirst());
    606         long contactId = c.getLong(c.getColumnIndex(RawContacts.CONTACT_ID));
    607         c.close();
    608         return contactId;
    609     }
    610 
    611     protected long queryPhotoId(long contactId) {
    612         Cursor c = queryContact(contactId);
    613         assertTrue(c.moveToFirst());
    614         long photoId = c.getInt(c.getColumnIndex(Contacts.PHOTO_ID));
    615         c.close();
    616         return photoId;
    617     }
    618 
    619     protected long queryPhotoFileId(long contactId) {
    620         return getStoredLongValue(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
    621                 Contacts.PHOTO_FILE_ID);
    622     }
    623 
    624     protected boolean queryRawContactIsStarred(long rawContactId) {
    625         Cursor c = queryRawContact(rawContactId);
    626         try {
    627             assertTrue(c.moveToFirst());
    628             return c.getLong(c.getColumnIndex(RawContacts.STARRED)) != 0;
    629         } finally {
    630             c.close();
    631         }
    632     }
    633 
    634     protected String queryDisplayName(long contactId) {
    635         Cursor c = queryContact(contactId);
    636         assertTrue(c.moveToFirst());
    637         String displayName = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME));
    638         c.close();
    639         return displayName;
    640     }
    641 
    642     protected String queryLookupKey(long contactId) {
    643         Cursor c = queryContact(contactId);
    644         assertTrue(c.moveToFirst());
    645         String lookupKey = c.getString(c.getColumnIndex(Contacts.LOOKUP_KEY));
    646         c.close();
    647         return lookupKey;
    648     }
    649 
    650     protected void assertAggregated(long rawContactId1, long rawContactId2) {
    651         long contactId1 = queryContactId(rawContactId1);
    652         long contactId2 = queryContactId(rawContactId2);
    653         assertTrue(contactId1 == contactId2);
    654     }
    655 
    656     protected void assertAggregated(long rawContactId1, long rawContactId2,
    657             String expectedDisplayName) {
    658         long contactId1 = queryContactId(rawContactId1);
    659         long contactId2 = queryContactId(rawContactId2);
    660         assertTrue(contactId1 == contactId2);
    661 
    662         String displayName = queryDisplayName(contactId1);
    663         assertEquals(expectedDisplayName, displayName);
    664     }
    665 
    666     protected void assertNotAggregated(long rawContactId1, long rawContactId2) {
    667         long contactId1 = queryContactId(rawContactId1);
    668         long contactId2 = queryContactId(rawContactId2);
    669         assertTrue(contactId1 != contactId2);
    670     }
    671 
    672     protected void assertStructuredName(long rawContactId, String prefix, String givenName,
    673             String middleName, String familyName, String suffix) {
    674         Uri uri = Uri.withAppendedPath(
    675                 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
    676                 RawContacts.Data.CONTENT_DIRECTORY);
    677 
    678         final String[] projection = new String[] {
    679                 StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
    680                 StructuredName.FAMILY_NAME, StructuredName.SUFFIX
    681         };
    682 
    683         Cursor c = mResolver.query(uri, projection, Data.MIMETYPE + "='"
    684                 + StructuredName.CONTENT_ITEM_TYPE + "'", null, null);
    685 
    686         assertTrue(c.moveToFirst());
    687         assertEquals(prefix, c.getString(0));
    688         assertEquals(givenName, c.getString(1));
    689         assertEquals(middleName, c.getString(2));
    690         assertEquals(familyName, c.getString(3));
    691         assertEquals(suffix, c.getString(4));
    692         c.close();
    693     }
    694 
    695     protected long assertSingleGroup(Long rowId, Account account, String sourceId, String title) {
    696         Cursor c = mResolver.query(Groups.CONTENT_URI, null, null, null, null);
    697         try {
    698             assertTrue(c.moveToNext());
    699             long actualRowId = assertGroup(c, rowId, account, sourceId, title);
    700             assertFalse(c.moveToNext());
    701             return actualRowId;
    702         } finally {
    703             c.close();
    704         }
    705     }
    706 
    707     protected long assertSingleGroupMembership(Long rowId, Long rawContactId, Long groupRowId,
    708             String sourceId) {
    709         Cursor c = mResolver.query(ContactsContract.Data.CONTENT_URI, null, null, null, null);
    710         try {
    711             assertTrue(c.moveToNext());
    712             long actualRowId = assertGroupMembership(c, rowId, rawContactId, groupRowId, sourceId);
    713             assertFalse(c.moveToNext());
    714             return actualRowId;
    715         } finally {
    716             c.close();
    717         }
    718     }
    719 
    720     protected long assertGroupMembership(Cursor c, Long rowId, Long rawContactId, Long groupRowId,
    721             String sourceId) {
    722         assertNullOrEquals(c, rowId, Data._ID);
    723         assertNullOrEquals(c, rawContactId, GroupMembership.RAW_CONTACT_ID);
    724         assertNullOrEquals(c, groupRowId, GroupMembership.GROUP_ROW_ID);
    725         assertNullOrEquals(c, sourceId, GroupMembership.GROUP_SOURCE_ID);
    726         return c.getLong(c.getColumnIndexOrThrow("_id"));
    727     }
    728 
    729     protected long assertGroup(Cursor c, Long rowId, Account account, String sourceId, String title) {
    730         assertNullOrEquals(c, rowId, Groups._ID);
    731         assertNullOrEquals(c, account);
    732         assertNullOrEquals(c, sourceId, Groups.SOURCE_ID);
    733         assertNullOrEquals(c, title, Groups.TITLE);
    734         return c.getLong(c.getColumnIndexOrThrow("_id"));
    735     }
    736 
    737     private void assertNullOrEquals(Cursor c, Account account) {
    738         if (account == NO_ACCOUNT) {
    739             return;
    740         }
    741         if (account == null) {
    742             assertTrue(c.isNull(c.getColumnIndexOrThrow(Groups.ACCOUNT_NAME)));
    743             assertTrue(c.isNull(c.getColumnIndexOrThrow(Groups.ACCOUNT_TYPE)));
    744         } else {
    745             assertEquals(account.name, c.getString(c.getColumnIndexOrThrow(Groups.ACCOUNT_NAME)));
    746             assertEquals(account.type, c.getString(c.getColumnIndexOrThrow(Groups.ACCOUNT_TYPE)));
    747         }
    748     }
    749 
    750     private void assertNullOrEquals(Cursor c, Long value, String columnName) {
    751         if (value != NO_LONG) {
    752             if (value == null) assertTrue(c.isNull(c.getColumnIndexOrThrow(columnName)));
    753             else assertEquals((long) value, c.getLong(c.getColumnIndexOrThrow(columnName)));
    754         }
    755     }
    756 
    757     private void assertNullOrEquals(Cursor c, String value, String columnName) {
    758         if (value != NO_STRING) {
    759             if (value == null) assertTrue(c.isNull(c.getColumnIndexOrThrow(columnName)));
    760             else assertEquals(value, c.getString(c.getColumnIndexOrThrow(columnName)));
    761         }
    762     }
    763 
    764     protected void assertSuperPrimary(Long dataId, boolean isSuperPrimary) {
    765         final String[] projection = new String[]{Data.MIMETYPE, Data._ID, Data.IS_SUPER_PRIMARY};
    766         Cursor c = mResolver.query(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
    767                 projection, null, null, null);
    768 
    769         c.moveToFirst();
    770         if (isSuperPrimary) {
    771             assertEquals(1, c.getInt(c.getColumnIndexOrThrow(Data.IS_SUPER_PRIMARY)));
    772         } else {
    773             assertEquals(0, c.getInt(c.getColumnIndexOrThrow(Data.IS_SUPER_PRIMARY)));
    774         }
    775 
    776     }
    777 
    778     protected void assertDataRow(ContentValues actual, String expectedMimetype,
    779             Object... expectedArguments) {
    780         assertEquals(actual.toString(), expectedMimetype, actual.getAsString(Data.MIMETYPE));
    781         for (int i = 0; i < expectedArguments.length; i += 2) {
    782             String columnName = (String) expectedArguments[i];
    783             Object expectedValue = expectedArguments[i + 1];
    784             if (expectedValue instanceof Uri) {
    785                 expectedValue = ContentUris.parseId((Uri) expectedValue);
    786             }
    787             if (expectedValue == null) {
    788                 assertNull(actual.toString(), actual.get(columnName));
    789             }
    790             if (expectedValue instanceof Long) {
    791                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
    792                         expectedValue, actual.getAsLong(columnName));
    793             } else if (expectedValue instanceof Integer) {
    794                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
    795                         expectedValue, actual.getAsInteger(columnName));
    796             } else if (expectedValue instanceof String) {
    797                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
    798                         expectedValue, actual.getAsString(columnName));
    799             } else {
    800                 assertEquals("mismatch at " + columnName + " from " + actual.toString(),
    801                         expectedValue, actual.get(columnName));
    802             }
    803         }
    804     }
    805 
    806     protected void assertNoRowsAndClose(Cursor c) {
    807         try {
    808             assertFalse(c.moveToNext());
    809         } finally {
    810             c.close();
    811         }
    812     }
    813 
    814     protected static class IdComparator implements Comparator<ContentValues> {
    815         @Override
    816         public int compare(ContentValues o1, ContentValues o2) {
    817             long id1 = o1.getAsLong(ContactsContract.Data._ID);
    818             long id2 = o2.getAsLong(ContactsContract.Data._ID);
    819             if (id1 == id2) return 0;
    820             return (id1 < id2) ? -1 : 1;
    821         }
    822     }
    823 
    824     protected ContentValues[] asSortedContentValuesArray(
    825             ArrayList<Entity.NamedContentValues> subValues) {
    826         ContentValues[] result = new ContentValues[subValues.size()];
    827         int i = 0;
    828         for (Entity.NamedContentValues subValue : subValues) {
    829             result[i] = subValue.values;
    830             i++;
    831         }
    832         Arrays.sort(result, new IdComparator());
    833         return result;
    834     }
    835 
    836     protected void assertDirty(Uri uri, boolean state) {
    837         Cursor c = mResolver.query(uri, new String[]{"dirty"}, null, null, null);
    838         assertTrue(c.moveToNext());
    839         assertEquals(state, c.getLong(0) != 0);
    840         assertFalse(c.moveToNext());
    841         c.close();
    842     }
    843 
    844     protected void assertMetadataDirty(Uri uri, boolean state) {
    845         Cursor c = mResolver.query(uri, new String[]{"metadata_dirty"}, null, null, null);
    846         assertTrue(c.moveToNext());
    847         assertEquals(state, c.getLong(0) != 0);
    848         assertFalse(c.moveToNext());
    849         c.close();
    850     }
    851 
    852     protected long getVersion(Uri uri) {
    853         Cursor c = mResolver.query(uri, new String[]{"version"}, null, null, null);
    854         assertTrue(c.moveToNext());
    855         long version = c.getLong(0);
    856         assertFalse(c.moveToNext());
    857         c.close();
    858         return version;
    859     }
    860 
    861     protected void clearDirty(Uri uri) {
    862         ContentValues values = new ContentValues();
    863         values.put("dirty", 0);
    864         mResolver.update(uri, values, null, null);
    865     }
    866 
    867     protected void clearMetadataDirty(Uri uri) {
    868         ContentValues values = new ContentValues();
    869         values.put("metadata_dirty", 0);
    870         mResolver.update(uri, values, null, null);
    871     }
    872 
    873     protected void storeValue(Uri contentUri, long id, String column, String value) {
    874         storeValue(ContentUris.withAppendedId(contentUri, id), column, value);
    875     }
    876 
    877     protected void storeValue(Uri contentUri, String column, String value) {
    878         ContentValues values = new ContentValues();
    879         values.put(column, value);
    880 
    881         mResolver.update(contentUri, values, null, null);
    882     }
    883 
    884     protected void storeValue(Uri contentUri, long id, String column, long value) {
    885         storeValue(ContentUris.withAppendedId(contentUri, id), column, value);
    886     }
    887 
    888     protected void storeValue(Uri contentUri, String column, long value) {
    889         ContentValues values = new ContentValues();
    890         values.put(column, value);
    891 
    892         mResolver.update(contentUri, values, null, null);
    893     }
    894 
    895     protected void assertStoredValue(Uri contentUri, long id, String column, Object expectedValue) {
    896         assertStoredValue(ContentUris.withAppendedId(contentUri, id), column, expectedValue);
    897     }
    898 
    899     protected void assertStoredValue(Uri rowUri, String column, Object expectedValue) {
    900         String value = getStoredValue(rowUri, column);
    901         if (expectedValue == null) {
    902             assertNull("Column value " + column, value);
    903         } else {
    904             assertEquals("Column value " + column, String.valueOf(expectedValue), value);
    905         }
    906     }
    907 
    908     protected void assertStoredValue(Uri rowUri, String selection, String[] selectionArgs,
    909             String column, Object expectedValue) {
    910         String value = getStoredValue(rowUri, selection, selectionArgs, column);
    911         if (expectedValue == null) {
    912             assertNull("Column value " + column, value);
    913         } else {
    914             assertEquals("Column value " + column, String.valueOf(expectedValue), value);
    915         }
    916     }
    917 
    918     protected String getStoredValue(Uri rowUri, String column) {
    919         return getStoredValue(rowUri, null, null, column);
    920     }
    921 
    922     protected String getStoredValue(Uri uri, String selection, String[] selectionArgs,
    923             String column) {
    924         String value = null;
    925         Cursor c = mResolver.query(uri, new String[] { column }, selection, selectionArgs, null);
    926         try {
    927             assertEquals("Record count for " + uri, 1, c.getCount());
    928 
    929             if (c.moveToFirst()) {
    930                 value = c.getString(c.getColumnIndex(column));
    931             }
    932         } finally {
    933             c.close();
    934         }
    935         return value;
    936     }
    937 
    938     protected Long getStoredLongValue(Uri uri, String selection, String[] selectionArgs,
    939             String column) {
    940         Long value = null;
    941         Cursor c = mResolver.query(uri, new String[] { column }, selection, selectionArgs, null);
    942         try {
    943             assertEquals("Record count", 1, c.getCount());
    944 
    945             if (c.moveToFirst()) {
    946                 value = c.getLong(c.getColumnIndex(column));
    947             }
    948         } finally {
    949             c.close();
    950         }
    951         return value;
    952     }
    953 
    954     protected Long getStoredLongValue(Uri uri, String column) {
    955         return getStoredLongValue(uri, null, null, column);
    956     }
    957 
    958     protected void assertStoredValues(Uri rowUri, ContentValues expectedValues) {
    959         assertStoredValues(rowUri, null, null, expectedValues);
    960     }
    961 
    962     protected void assertStoredValues(Uri rowUri, ContentValues... expectedValues) {
    963         assertStoredValues(rowUri, null, null, expectedValues);
    964     }
    965 
    966     protected void assertStoredValues(Uri rowUri, String selection, String[] selectionArgs,
    967             ContentValues expectedValues) {
    968         Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null);
    969         try {
    970             assertEquals("Record count", 1, c.getCount());
    971             c.moveToFirst();
    972             assertCursorValues(c, expectedValues);
    973         } catch (Error e) {
    974             TestUtils.dumpCursor(c);
    975             throw e;
    976         } finally {
    977             c.close();
    978         }
    979     }
    980 
    981     protected void assertContainsValues(Uri rowUri, ContentValues expectedValues) {
    982         Cursor c = mResolver.query(rowUri, null, null, null, null);
    983         try {
    984             assertEquals("Record count", 1, c.getCount());
    985             c.moveToFirst();
    986             assertCursorValuesPartialMatch(c, expectedValues);
    987         } catch (Error e) {
    988             TestUtils.dumpCursor(c);
    989             throw e;
    990         } finally {
    991             c.close();
    992         }
    993     }
    994 
    995     protected void assertStoredValuesWithProjection(Uri rowUri, ContentValues expectedValues) {
    996         assertStoredValuesWithProjection(rowUri, new ContentValues[] {expectedValues});
    997     }
    998 
    999     protected void assertStoredValuesWithProjection(Uri rowUri, ContentValues... expectedValues) {
   1000         assertTrue("Need at least one ContentValues for this test", expectedValues.length > 0);
   1001         Cursor c = mResolver.query(rowUri, buildProjection(expectedValues[0]), null, null, null);
   1002         try {
   1003             assertEquals("Record count", expectedValues.length, c.getCount());
   1004             c.moveToFirst();
   1005             assertCursorValues(c, expectedValues);
   1006         } catch (Error e) {
   1007             TestUtils.dumpCursor(c);
   1008             throw e;
   1009         } finally {
   1010             c.close();
   1011         }
   1012     }
   1013 
   1014     protected void assertStoredValues(
   1015             Uri rowUri, String selection, String[] selectionArgs, ContentValues... expectedValues) {
   1016         assertStoredValues(mResolver.query(rowUri, null, selection, selectionArgs, null),
   1017                 expectedValues);
   1018     }
   1019 
   1020     private void assertStoredValues(Cursor c, ContentValues... expectedValues) {
   1021         try {
   1022             assertEquals("Record count", expectedValues.length, c.getCount());
   1023             assertCursorValues(c, expectedValues);
   1024         } catch (Error e) {
   1025             TestUtils.dumpCursor(c);
   1026             throw e;
   1027         } finally {
   1028             c.close();
   1029         }
   1030     }
   1031 
   1032     /**
   1033      * A variation of {@link #assertStoredValues}, but it queries directly to the DB.
   1034      */
   1035     protected void assertStoredValuesDb(
   1036             String sql, String[] selectionArgs, ContentValues... expectedValues) {
   1037         SQLiteDatabase db = ((ContactsProvider2) getProvider()).getDatabaseHelper()
   1038                 .getReadableDatabase();
   1039         assertStoredValues(db.rawQuery(sql, selectionArgs), expectedValues);
   1040     }
   1041 
   1042     protected void assertStoredValuesOrderly(Uri rowUri, ContentValues... expectedValues) {
   1043         assertStoredValuesOrderly(rowUri, null, null, expectedValues);
   1044     }
   1045 
   1046     protected void assertStoredValuesOrderly(Uri rowUri, String selection,
   1047             String[] selectionArgs, ContentValues... expectedValues) {
   1048         Cursor c = mResolver.query(rowUri, null, selection, selectionArgs, null);
   1049         try {
   1050             assertEquals("Record count", expectedValues.length, c.getCount());
   1051             assertCursorValuesOrderly(c, expectedValues);
   1052         } catch (Error e) {
   1053             TestUtils.dumpCursor(c);
   1054             throw e;
   1055         } finally {
   1056             c.close();
   1057         }
   1058     }
   1059 
   1060     /**
   1061      * Constructs a selection (where clause) out of all supplied values, uses it
   1062      * to query the provider and verifies that a single row is returned and it
   1063      * has the same values as requested.
   1064      */
   1065     protected void assertSelection(Uri uri, ContentValues values, String idColumn, long id) {
   1066         assertSelection(uri, values, idColumn, id, null);
   1067     }
   1068 
   1069     public void assertSelectionWithProjection(Uri uri, ContentValues values, String idColumn,
   1070             long id) {
   1071         assertSelection(uri, values, idColumn, id, buildProjection(values));
   1072     }
   1073 
   1074     private void assertSelection(Uri uri, ContentValues values, String idColumn, long id,
   1075             String[] projection) {
   1076         StringBuilder sb = new StringBuilder();
   1077         ArrayList<String> selectionArgs = new ArrayList<String>(values.size());
   1078         if (idColumn != null) {
   1079             sb.append(idColumn).append("=").append(id);
   1080         }
   1081         Set<Map.Entry<String, Object>> entries = values.valueSet();
   1082         for (Map.Entry<String, Object> entry : entries) {
   1083             String column = entry.getKey();
   1084             Object value = entry.getValue();
   1085             if (sb.length() != 0) {
   1086                 sb.append(" AND ");
   1087             }
   1088             sb.append(column);
   1089             if (value == null) {
   1090                 sb.append(" IS NULL");
   1091             } else {
   1092                 sb.append("=?");
   1093                 selectionArgs.add(String.valueOf(value));
   1094             }
   1095         }
   1096 
   1097         Cursor c = mResolver.query(uri, projection, sb.toString(), selectionArgs.toArray(new String[0]),
   1098                 null);
   1099         try {
   1100             assertEquals("Record count", 1, c.getCount());
   1101             c.moveToFirst();
   1102             assertCursorValues(c, values);
   1103         } catch (Error e) {
   1104             TestUtils.dumpCursor(c);
   1105 
   1106             // Dump with no selection.
   1107             TestUtils.dumpUri(mResolver, uri);
   1108             throw e;
   1109         } finally {
   1110             c.close();
   1111         }
   1112     }
   1113 
   1114     protected void assertCursorValue(Cursor cursor, String column, Object expectedValue) {
   1115         String actualValue = cursor.getString(cursor.getColumnIndex(column));
   1116         assertEquals("Column " + column, String.valueOf(expectedValue),
   1117                 String.valueOf(actualValue));
   1118     }
   1119 
   1120     protected void assertCursorValues(Cursor cursor, ContentValues expectedValues) {
   1121         StringBuilder message = new StringBuilder();
   1122         boolean result = equalsWithExpectedValues(cursor, expectedValues, message);
   1123         assertTrue(message.toString(), result);
   1124     }
   1125 
   1126     protected void assertCursorValuesPartialMatch(Cursor cursor, ContentValues expectedValues) {
   1127         StringBuilder message = new StringBuilder();
   1128         boolean result = expectedValuePartiallyMatches(cursor, expectedValues, message);
   1129         assertTrue(message.toString(), result);
   1130     }
   1131 
   1132     protected void assertCursorHasAnyRecordMatch(Cursor cursor, ContentValues expectedValues) {
   1133         final StringBuilder message = new StringBuilder();
   1134         boolean found = false;
   1135         cursor.moveToPosition(-1);
   1136         while (cursor.moveToNext()) {
   1137             message.setLength(0);
   1138             final int pos = cursor.getPosition();
   1139             found = equalsWithExpectedValues(cursor, expectedValues, message);
   1140             if (found) {
   1141                 break;
   1142             }
   1143         }
   1144         assertTrue("Expected values can not be found " + expectedValues + "," + message.toString(),
   1145                 found);
   1146     }
   1147 
   1148     protected void assertCursorValues(Cursor cursor, ContentValues... expectedValues) {
   1149         StringBuilder message = new StringBuilder();
   1150 
   1151         // In case if expectedValues contains multiple identical values, remember which cursor
   1152         // rows are "consumed" to prevent multiple ContentValues from hitting the same row.
   1153         final BitSet used = new BitSet(cursor.getCount());
   1154 
   1155         for (ContentValues v : expectedValues) {
   1156             boolean found = false;
   1157             cursor.moveToPosition(-1);
   1158             while (cursor.moveToNext()) {
   1159                 final int pos = cursor.getPosition();
   1160                 if (used.get(pos)) continue;
   1161                 found = equalsWithExpectedValues(cursor, v, message);
   1162                 if (found) {
   1163                     used.set(pos);
   1164                     break;
   1165                 }
   1166             }
   1167             assertTrue("Expected values can not be found " + v + "," + message.toString(), found);
   1168         }
   1169     }
   1170 
   1171     public static void assertCursorValuesOrderly(Cursor cursor, ContentValues... expectedValues) {
   1172         StringBuilder message = new StringBuilder();
   1173         cursor.moveToPosition(-1);
   1174         for (ContentValues v : expectedValues) {
   1175             assertTrue(cursor.moveToNext());
   1176             boolean ok = equalsWithExpectedValues(cursor, v, message);
   1177             assertTrue("ContentValues didn't match.  Pos=" + cursor.getPosition() + ", values=" +
   1178                     v + message.toString(), ok);
   1179         }
   1180     }
   1181 
   1182     private boolean expectedValuePartiallyMatches(Cursor cursor, ContentValues expectedValues,
   1183             StringBuilder msgBuffer) {
   1184         for (String column : expectedValues.keySet()) {
   1185             int index = cursor.getColumnIndex(column);
   1186             if (index == -1) {
   1187                 msgBuffer.append(" No such column: ").append(column);
   1188                 return false;
   1189             }
   1190             String expectedValue = expectedValues.getAsString(column);
   1191             String value = cursor.getString(cursor.getColumnIndex(column));
   1192             if (value != null && !value.contains(expectedValue)) {
   1193                 msgBuffer.append(" Column value ").append(column).append(" expected to contain <")
   1194                         .append(expectedValue).append(">, but was <").append(value).append('>');
   1195                 return false;
   1196             }
   1197         }
   1198         return true;
   1199     }
   1200 
   1201     private static boolean equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues,
   1202             StringBuilder msgBuffer) {
   1203         for (String column : expectedValues.keySet()) {
   1204             int index = cursor.getColumnIndex(column);
   1205             if (index == -1) {
   1206                 msgBuffer.append(" No such column: ").append(column);
   1207                 return false;
   1208             }
   1209             Object expectedValue = expectedValues.get(column);
   1210             String value;
   1211             if (expectedValue instanceof byte[]) {
   1212                 expectedValue = Hex.encodeHex((byte[])expectedValue, false);
   1213                 value = Hex.encodeHex(cursor.getBlob(index), false);
   1214             } else {
   1215                 expectedValue = expectedValues.getAsString(column);
   1216                 value = cursor.getString(cursor.getColumnIndex(column));
   1217             }
   1218             if (expectedValue != null && !expectedValue.equals(value) || value != null
   1219                     && !value.equals(expectedValue)) {
   1220                 msgBuffer
   1221                         .append(" Column value ")
   1222                         .append(column)
   1223                         .append(" expected <")
   1224                         .append(expectedValue)
   1225                         .append(">, but was <")
   1226                         .append(value)
   1227                         .append('>');
   1228                 return false;
   1229             }
   1230         }
   1231         return true;
   1232     }
   1233 
   1234     private static final String[] DATA_USAGE_PROJECTION =
   1235             new String[] {Data.DATA1, Data.TIMES_USED, Data.LAST_TIME_USED};
   1236 
   1237     protected void assertDataUsageCursorContains(Uri uri, String data1, int timesUsed,
   1238             int lastTimeUsed) {
   1239         final Cursor cursor = mResolver.query(uri, DATA_USAGE_PROJECTION, null, null,
   1240                 null);
   1241         try {
   1242             dumpCursor(cursor);
   1243             assertCursorHasAnyRecordMatch(cursor, cv(Data.DATA1, data1, Data.TIMES_USED, timesUsed,
   1244                     Data.LAST_TIME_USED, lastTimeUsed));
   1245         } finally {
   1246             cursor.close();
   1247         }
   1248     }
   1249 
   1250     private String[] buildProjection(ContentValues values) {
   1251         String[] projection = new String[values.size()];
   1252         Iterator<Entry<String, Object>> iter = values.valueSet().iterator();
   1253         for (int i = 0; i < projection.length; i++) {
   1254             projection[i] = iter.next().getKey();
   1255         }
   1256         return projection;
   1257     }
   1258 
   1259     protected int getCount(Uri uri) {
   1260         return getCount(uri, null, null);
   1261     }
   1262 
   1263     protected int getCount(Uri uri, String selection, String[] selectionArgs) {
   1264         Cursor c = mResolver.query(uri, null, selection, selectionArgs, null);
   1265         try {
   1266             return c.getCount();
   1267         } finally {
   1268             c.close();
   1269         }
   1270     }
   1271 
   1272     public static void dump(ContentResolver resolver, boolean aggregatedOnly) {
   1273         String[] projection = new String[] {
   1274                 Contacts._ID,
   1275                 Contacts.DISPLAY_NAME
   1276         };
   1277         String selection = null;
   1278         if (aggregatedOnly) {
   1279             selection = Contacts._ID
   1280                     + " IN (SELECT contact_id" +
   1281                     		" FROM raw_contacts GROUP BY contact_id HAVING count(*) > 1)";
   1282         }
   1283 
   1284         Cursor c = resolver.query(Contacts.CONTENT_URI, projection, selection, null,
   1285                 Contacts.DISPLAY_NAME);
   1286         while(c.moveToNext()) {
   1287             long contactId = c.getLong(0);
   1288             Log.i("Contact   ", String.format("%5d %s", contactId, c.getString(1)));
   1289             dumpRawContacts(resolver, contactId);
   1290             Log.i("          ", ".");
   1291         }
   1292         c.close();
   1293     }
   1294 
   1295     private static void dumpRawContacts(ContentResolver resolver, long contactId) {
   1296         String[] projection = new String[] {
   1297                 RawContacts._ID,
   1298         };
   1299         Cursor c = resolver.query(RawContacts.CONTENT_URI, projection, RawContacts.CONTACT_ID + "="
   1300                 + contactId, null, null);
   1301         while(c.moveToNext()) {
   1302             long rawContactId = c.getLong(0);
   1303             Log.i("RawContact", String.format("      %-5d", rawContactId));
   1304             dumpData(resolver, rawContactId);
   1305         }
   1306         c.close();
   1307     }
   1308 
   1309     private static void dumpData(ContentResolver resolver, long rawContactId) {
   1310         String[] projection = new String[] {
   1311                 Data.MIMETYPE,
   1312                 Data.DATA1,
   1313                 Data.DATA2,
   1314                 Data.DATA3,
   1315         };
   1316         Cursor c = resolver.query(Data.CONTENT_URI, projection, Data.RAW_CONTACT_ID + "="
   1317                 + rawContactId, null, Data.MIMETYPE);
   1318         while(c.moveToNext()) {
   1319             String mimetype = c.getString(0);
   1320             if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
   1321                 Log.i("Photo     ", "");
   1322             } else {
   1323                 mimetype = mimetype.substring(mimetype.indexOf('/') + 1);
   1324                 Log.i("Data      ", String.format("            %-10s %s,%s,%s", mimetype,
   1325                         c.getString(1), c.getString(2), c.getString(3)));
   1326             }
   1327         }
   1328         c.close();
   1329     }
   1330 
   1331     protected void assertNetworkNotified(boolean expected) {
   1332         assertEquals(expected, (getContactsProvider()).isNetworkNotified());
   1333     }
   1334 
   1335     protected void assertMetadataNetworkNotified(boolean expected) {
   1336         assertEquals(expected, (getContactsProvider()).isMetadataNetworkNotified());
   1337     }
   1338 
   1339     protected void assertProjection(Uri uri, String[] expectedProjection) {
   1340         Cursor cursor = mResolver.query(uri, null, "0", null, null);
   1341         String[] actualProjection = cursor.getColumnNames();
   1342         MoreAsserts.assertEquals("Incorrect projection for URI: " + uri,
   1343                 Sets.newHashSet(expectedProjection), Sets.newHashSet(actualProjection));
   1344         cursor.close();
   1345     }
   1346 
   1347     protected void assertContainProjection(Uri uri, String[] mustHaveProjection) {
   1348         Cursor cursor = mResolver.query(uri, null, "0", null, null);
   1349         String[] actualProjection = cursor.getColumnNames();
   1350         Set<String> actualProjectionSet = Sets.newHashSet(actualProjection);
   1351         Set<String> mustHaveProjectionSet = Sets.newHashSet(mustHaveProjection);
   1352         actualProjectionSet.retainAll(mustHaveProjectionSet);
   1353         MoreAsserts.assertEquals(mustHaveProjectionSet, actualProjectionSet);
   1354         cursor.close();
   1355     }
   1356 
   1357     protected void assertRowCount(int expectedCount, Uri uri, String selection, String[] args) {
   1358         Cursor cursor = mResolver.query(uri, null, selection, args, null);
   1359 
   1360         try {
   1361             assertEquals(expectedCount, cursor.getCount());
   1362         } catch (Error e) {
   1363             TestUtils.dumpCursor(cursor);
   1364             throw e;
   1365         } finally {
   1366             cursor.close();
   1367         }
   1368     }
   1369 
   1370     protected void assertLastModified(Uri uri, long time) {
   1371         Cursor c = mResolver.query(uri, null, null, null, null);
   1372         c.moveToFirst();
   1373         int index = c.getColumnIndex(CallLog.Calls.LAST_MODIFIED);
   1374         long timeStamp = c.getLong(index);
   1375         assertEquals(timeStamp, time);
   1376     }
   1377 
   1378     protected void setTimeForTest(Long time) {
   1379         Uri uri = Calls.CONTENT_URI.buildUpon()
   1380                 .appendQueryParameter(CallLogProvider.PARAM_KEY_QUERY_FOR_TESTING, "1")
   1381                 .appendQueryParameter(CallLogProvider.PARAM_KEY_SET_TIME_FOR_TESTING,
   1382                         time == null ? "null" : time.toString())
   1383                 .build();
   1384         mResolver.query(uri, null, null, null, null);
   1385     }
   1386 
   1387     protected Uri insertRawContact(ContentValues values) {
   1388         return TestUtils.insertRawContact(mResolver,
   1389                 getContactsProvider().getDatabaseHelper(), values);
   1390     }
   1391 
   1392     protected Uri insertProfileRawContact(ContentValues values) {
   1393         return TestUtils.insertProfileRawContact(mResolver,
   1394                 getContactsProvider().getProfileProviderForTest().getDatabaseHelper(), values);
   1395     }
   1396 
   1397     /**
   1398      * A contact in the database, and the attributes used to create it.  Construct using
   1399      * {@link GoldenContactBuilder#build()}.
   1400      */
   1401     public final class GoldenContact {
   1402 
   1403         private final long rawContactId;
   1404 
   1405         private final long contactId;
   1406 
   1407         private final String givenName;
   1408 
   1409         private final String familyName;
   1410 
   1411         private final String nickname;
   1412 
   1413         private final byte[] photo;
   1414 
   1415         private final String company;
   1416 
   1417         private final String title;
   1418 
   1419         private final String phone;
   1420 
   1421         private final String email;
   1422 
   1423         private GoldenContact(GoldenContactBuilder builder, long rawContactId, long contactId) {
   1424 
   1425             this.rawContactId = rawContactId;
   1426             this.contactId = contactId;
   1427             givenName = builder.givenName;
   1428             familyName = builder.familyName;
   1429             nickname = builder.nickname;
   1430             photo = builder.photo;
   1431             company = builder.company;
   1432             title = builder.title;
   1433             phone = builder.phone;
   1434             email = builder.email;
   1435         }
   1436 
   1437         public void delete() {
   1438             Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
   1439             mResolver.delete(rawContactUri, null, null);
   1440         }
   1441 
   1442         /**
   1443          * Returns the index of the contact in table "raw_contacts"
   1444          */
   1445         public long getRawContactId() {
   1446             return rawContactId;
   1447         }
   1448 
   1449         /**
   1450          * Returns the index of the contact in table "contacts"
   1451          */
   1452         public long getContactId() {
   1453             return contactId;
   1454         }
   1455 
   1456         /**
   1457          * Returns the lookup key for the contact.
   1458          */
   1459         public String getLookupKey() {
   1460             return queryLookupKey(contactId);
   1461         }
   1462 
   1463         /**
   1464          * Returns the contact's given name.
   1465          */
   1466         public String getGivenName() {
   1467             return givenName;
   1468         }
   1469 
   1470         /**
   1471          * Returns the contact's family name.
   1472          */
   1473         public String getFamilyName() {
   1474             return familyName;
   1475         }
   1476 
   1477         /**
   1478          * Returns the contact's nickname.
   1479          */
   1480         public String getNickname() {
   1481             return nickname;
   1482         }
   1483 
   1484         /**
   1485          * Return's the contact's photo
   1486          */
   1487         public byte[] getPhoto() {
   1488             return photo;
   1489         }
   1490 
   1491         /**
   1492          * Return's the company at which the contact works.
   1493          */
   1494         public String getCompany() {
   1495             return company;
   1496         }
   1497 
   1498         /**
   1499          * Returns the contact's job title.
   1500          */
   1501         public String getTitle() {
   1502             return title;
   1503         }
   1504 
   1505         /**
   1506          * Returns the contact's phone number
   1507          */
   1508         public String getPhone() {
   1509             return phone;
   1510         }
   1511 
   1512         /**
   1513          * Returns the contact's email address
   1514          */
   1515         public String getEmail() {
   1516             return email;
   1517         }
   1518      }
   1519 
   1520     /**
   1521      * Builds {@link GoldenContact} objects.  Unspecified boolean objects default to false.
   1522      * Unspecified String objects default to null.
   1523      */
   1524     public final class GoldenContactBuilder {
   1525 
   1526         private String givenName;
   1527 
   1528         private String familyName;
   1529 
   1530         private String nickname;
   1531 
   1532         private byte[] photo;
   1533 
   1534         private String company;
   1535 
   1536         private String title;
   1537 
   1538         private String phone;
   1539 
   1540         private String email;
   1541 
   1542         /**
   1543          * The contact's given and family names.
   1544          *
   1545          * TODO(dplotnikov): inline, or should we require them to set both names if they set either?
   1546          */
   1547         public GoldenContactBuilder name(String givenName, String familyName) {
   1548             return givenName(givenName).familyName(familyName);
   1549         }
   1550 
   1551         /**
   1552          * The contact's given name.
   1553          */
   1554         public GoldenContactBuilder givenName(String value) {
   1555             givenName = value;
   1556             return this;
   1557         }
   1558 
   1559         /**
   1560          * The contact's family name.
   1561          */
   1562         public GoldenContactBuilder familyName(String value) {
   1563             familyName = value;
   1564             return this;
   1565         }
   1566 
   1567         /**
   1568          * The contact's nickname.
   1569          */
   1570         public GoldenContactBuilder nickname(String value) {
   1571             nickname = value;
   1572             return this;
   1573         }
   1574 
   1575         /**
   1576          * The contact's photo.
   1577          */
   1578         public GoldenContactBuilder photo(byte[] value) {
   1579             photo = value;
   1580             return this;
   1581         }
   1582 
   1583         /**
   1584          * The company at which the contact works.
   1585          */
   1586         public GoldenContactBuilder company(String value) {
   1587             company = value;
   1588             return this;
   1589         }
   1590 
   1591         /**
   1592          * The contact's job title.
   1593          */
   1594         public GoldenContactBuilder title(String value) {
   1595             title = value;
   1596             return this;
   1597         }
   1598 
   1599         /**
   1600          * The contact's phone number.
   1601          */
   1602         public GoldenContactBuilder phone(String value) {
   1603             phone = value;
   1604             return this;
   1605         }
   1606 
   1607         /**
   1608          * The contact's email address; also sets their IM status to {@link StatusUpdates#OFFLINE}
   1609          * with a presence of "Coding for Android".
   1610          */
   1611         public GoldenContactBuilder email(String value) {
   1612             email = value;
   1613             return this;
   1614         }
   1615 
   1616         /**
   1617          * Builds the {@link GoldenContact} specified by this builder.
   1618          */
   1619         public GoldenContact build() {
   1620 
   1621             final long groupId = createGroup(mAccount, "gsid1", "title1");
   1622 
   1623             long rawContactId = RawContactUtil.createRawContact(mResolver);
   1624             insertGroupMembership(rawContactId, groupId);
   1625 
   1626             if (givenName != null || familyName != null) {
   1627                 DataUtil.insertStructuredName(mResolver, rawContactId, givenName, familyName);
   1628             }
   1629             if (nickname != null) {
   1630                 insertNickname(rawContactId, nickname);
   1631             }
   1632             if (photo != null) {
   1633                 insertPhoto(rawContactId);
   1634             }
   1635             if (company != null || title != null) {
   1636                 insertOrganization(rawContactId);
   1637             }
   1638             if (email != null) {
   1639                 insertEmail(rawContactId);
   1640             }
   1641             if (phone != null) {
   1642                 insertPhone(rawContactId);
   1643             }
   1644 
   1645             long contactId = queryContactId(rawContactId);
   1646 
   1647             return new GoldenContact(this, rawContactId, contactId);
   1648         }
   1649 
   1650         private void insertPhoto(long rawContactId) {
   1651             ContentValues values = new ContentValues();
   1652             values.put(Data.RAW_CONTACT_ID, rawContactId);
   1653             values.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
   1654             values.put(Photo.PHOTO, photo);
   1655             mResolver.insert(Data.CONTENT_URI, values);
   1656         }
   1657 
   1658         private void insertOrganization(long rawContactId) {
   1659 
   1660             ContentValues values = new ContentValues();
   1661             values.put(Data.RAW_CONTACT_ID, rawContactId);
   1662             values.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
   1663             values.put(Organization.TYPE, Organization.TYPE_WORK);
   1664             if (company != null) {
   1665                 values.put(Organization.COMPANY, company);
   1666             }
   1667             if (title != null) {
   1668                 values.put(Organization.TITLE, title);
   1669             }
   1670             mResolver.insert(Data.CONTENT_URI, values);
   1671         }
   1672 
   1673         private void insertEmail(long rawContactId) {
   1674 
   1675             ContentValues values = new ContentValues();
   1676             values.put(Data.RAW_CONTACT_ID, rawContactId);
   1677             values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
   1678             values.put(Email.TYPE, Email.TYPE_WORK);
   1679             values.put(Email.DATA, "foo (at) acme.com");
   1680             mResolver.insert(Data.CONTENT_URI, values);
   1681 
   1682             int protocol = Im.PROTOCOL_GOOGLE_TALK;
   1683 
   1684             values.clear();
   1685             values.put(StatusUpdates.PROTOCOL, protocol);
   1686             values.put(StatusUpdates.IM_HANDLE, email);
   1687             values.put(StatusUpdates.IM_ACCOUNT, "foo");
   1688             values.put(StatusUpdates.PRESENCE_STATUS, StatusUpdates.OFFLINE);
   1689             values.put(StatusUpdates.CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA);
   1690             values.put(StatusUpdates.PRESENCE_CUSTOM_STATUS, "Coding for Android");
   1691             mResolver.insert(StatusUpdates.CONTENT_URI, values);
   1692         }
   1693 
   1694         private void insertPhone(long rawContactId) {
   1695             ContentValues values = new ContentValues();
   1696             values.put(Data.RAW_CONTACT_ID, rawContactId);
   1697             values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1698             values.put(Data.IS_PRIMARY, 1);
   1699             values.put(Phone.TYPE, Phone.TYPE_HOME);
   1700             values.put(Phone.NUMBER, phone);
   1701             mResolver.insert(Data.CONTENT_URI, values);
   1702         }
   1703     }
   1704 }
   1705