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 android.accounts.Account;
     20 import android.accounts.AccountManager;
     21 import android.accounts.AccountManagerCallback;
     22 import android.accounts.AccountManagerFuture;
     23 import android.accounts.AuthenticatorException;
     24 import android.accounts.OnAccountsUpdateListener;
     25 import android.accounts.OperationCanceledException;
     26 import android.app.admin.DevicePolicyManager;
     27 import android.content.ContentProvider;
     28 import android.content.ContentResolver;
     29 import android.content.ContentUris;
     30 import android.content.ContentValues;
     31 import android.content.Context;
     32 import android.content.ContextWrapper;
     33 import android.content.Intent;
     34 import android.content.SharedPreferences;
     35 import android.content.pm.ApplicationInfo;
     36 import android.content.pm.PackageManager;
     37 import android.content.pm.ProviderInfo;
     38 import android.content.pm.UserInfo;
     39 import android.content.res.Configuration;
     40 import android.content.res.Resources;
     41 import android.database.Cursor;
     42 import android.location.Country;
     43 import android.location.CountryDetector;
     44 import android.location.CountryListener;
     45 import android.net.Uri;
     46 import android.os.Bundle;
     47 import android.os.Handler;
     48 import android.os.IUserManager;
     49 import android.os.Looper;
     50 import android.os.UserHandle;
     51 import android.os.UserManager;
     52 import android.provider.BaseColumns;
     53 import android.provider.ContactsContract;
     54 import android.provider.ContactsContract.AggregationExceptions;
     55 import android.provider.ContactsContract.CommonDataKinds;
     56 import android.provider.ContactsContract.CommonDataKinds.Email;
     57 import android.provider.ContactsContract.CommonDataKinds.Phone;
     58 import android.provider.ContactsContract.Contacts;
     59 import android.provider.ContactsContract.Data;
     60 import android.provider.ContactsContract.RawContacts;
     61 import android.provider.ContactsContract.StatusUpdates;
     62 import android.test.IsolatedContext;
     63 import android.test.RenamingDelegatingContext;
     64 import android.test.mock.MockContentResolver;
     65 import android.test.mock.MockContext;
     66 import android.util.Log;
     67 
     68 import com.android.providers.contacts.util.MockSharedPreferences;
     69 import com.google.android.collect.Sets;
     70 
     71 import java.io.File;
     72 import java.io.IOException;
     73 import java.util.ArrayList;
     74 import java.util.Arrays;
     75 import java.util.List;
     76 import java.util.Locale;
     77 import java.util.Set;
     78 
     79 /**
     80  * Helper class that encapsulates an "actor" which is owned by a specific
     81  * package name. It correctly maintains a wrapped {@link Context} and an
     82  * attached {@link MockContentResolver}. Multiple actors can be used to test
     83  * security scenarios between multiple packages.
     84  */
     85 public class ContactsActor {
     86     private static final String FILENAME_PREFIX = "test.";
     87 
     88     public static final String PACKAGE_GREY = "edu.example.grey";
     89     public static final String PACKAGE_RED = "net.example.red";
     90     public static final String PACKAGE_GREEN = "com.example.green";
     91     public static final String PACKAGE_BLUE = "org.example.blue";
     92 
     93     public Context context;
     94     public String packageName;
     95     public MockContentResolver resolver;
     96     public ContentProvider provider;
     97     private Country mMockCountry = new Country("us", 0);
     98 
     99     private Account[] mAccounts = new Account[0];
    100 
    101     private Set<String> mGrantedPermissions = Sets.newHashSet();
    102     private final Set<Uri> mGrantedUriPermissions = Sets.newHashSet();
    103 
    104     private CountryDetector mMockCountryDetector = new CountryDetector(null){
    105         @Override
    106         public Country detectCountry() {
    107             return mMockCountry;
    108         }
    109 
    110         @Override
    111         public void addCountryListener(CountryListener listener, Looper looper) {
    112         }
    113     };
    114 
    115     private AccountManager mMockAccountManager;
    116 
    117     private class MockAccountManager extends AccountManager {
    118         public MockAccountManager(Context conteact) {
    119             super(context, null, null);
    120         }
    121 
    122         @Override
    123         public void addOnAccountsUpdatedListener(OnAccountsUpdateListener listener,
    124                 Handler handler, boolean updateImmediately) {
    125             // do nothing
    126         }
    127 
    128         @Override
    129         public Account[] getAccounts() {
    130             return mAccounts;
    131         }
    132 
    133         @Override
    134         public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
    135                 final String type, final String[] features,
    136                 AccountManagerCallback<Account[]> callback, Handler handler) {
    137             return null;
    138         }
    139 
    140         @Override
    141         public String blockingGetAuthToken(Account account, String authTokenType,
    142                 boolean notifyAuthFailure)
    143                 throws OperationCanceledException, IOException, AuthenticatorException {
    144             return null;
    145         }
    146     }
    147 
    148     public MockUserManager mockUserManager;
    149 
    150     public static class MockUserManager extends UserManager {
    151         public static UserInfo createUserInfo(String name, int id, int groupId, int flags) {
    152             final UserInfo ui = new UserInfo();
    153             ui.name = name;
    154             ui.id = id;
    155             ui.profileGroupId = groupId;
    156             ui.flags = flags | UserInfo.FLAG_INITIALIZED;
    157             return ui;
    158         }
    159 
    160         public static final UserInfo PRIMARY_USER = createUserInfo("primary", 0, 0,
    161                 UserInfo.FLAG_PRIMARY | UserInfo.FLAG_ADMIN);
    162         public static final UserInfo CORP_USER = createUserInfo("corp", 10, 0,
    163                 UserInfo.FLAG_MANAGED_PROFILE);
    164         public static final UserInfo SECONDARY_USER = createUserInfo("2nd", 11, 11, 0);
    165 
    166         /** "My" user.  Set it to change the current user. */
    167         public int myUser = 0;
    168 
    169         private ArrayList<UserInfo> mUsers = new ArrayList<>();
    170 
    171         public MockUserManager(Context context) {
    172             super(context, /* IUserManager */ null);
    173 
    174             mUsers.add(PRIMARY_USER); // Add the primary user.
    175         }
    176 
    177         /** Replaces users. */
    178         public void setUsers(UserInfo... users) {
    179             mUsers.clear();
    180             for (UserInfo ui : users) {
    181                 mUsers.add(ui);
    182             }
    183         }
    184 
    185         @Override
    186         public int getUserHandle() {
    187             return myUser;
    188         }
    189 
    190         @Override
    191         public UserInfo getUserInfo(int userHandle) {
    192             for (UserInfo ui : mUsers) {
    193                 if (ui.id == userHandle) {
    194                     return ui;
    195                 }
    196             }
    197             return null;
    198         }
    199 
    200         @Override
    201         public UserInfo getProfileParent(int userHandle) {
    202             final UserInfo child = getUserInfo(userHandle);
    203             if (child == null) {
    204                 return null;
    205             }
    206             for (UserInfo ui : mUsers) {
    207                 if (ui.id != userHandle && ui.id == child.profileGroupId) {
    208                     return ui;
    209                 }
    210             }
    211             return null;
    212         }
    213 
    214         @Override
    215         public List<UserInfo> getUsers() {
    216             return mUsers;
    217         }
    218 
    219         @Override
    220         public Bundle getUserRestrictions(UserHandle userHandle) {
    221             return new Bundle();
    222         }
    223     }
    224 
    225     /**
    226      * A context wrapper that reports a different user id.
    227      *
    228      * TODO This should override getSystemService() and returns a UserManager that returns the
    229      * same, altered user ID too.
    230      */
    231     public static class AlteringUserContext extends ContextWrapper {
    232         private final int mUserId;
    233 
    234         public AlteringUserContext(Context base, int userId) {
    235             super(base);
    236             mUserId = userId;
    237         }
    238 
    239         @Override
    240         public int getUserId() {
    241             return mUserId;
    242         }
    243     }
    244 
    245     private IsolatedContext mProviderContext;
    246 
    247     /**
    248      * Create an "actor" using the given parent {@link Context} and the specific
    249      * package name. Internally, all {@link Context} method calls are passed to
    250      * a new instance of {@link RestrictionMockContext}, which stubs out the
    251      * security infrastructure.
    252      */
    253     public ContactsActor(final Context overallContext, String packageName,
    254             Class<? extends ContentProvider> providerClass, String authority) throws Exception {
    255         resolver = new MockContentResolver();
    256         context = new RestrictionMockContext(overallContext, packageName, resolver,
    257                 mGrantedPermissions, mGrantedUriPermissions);
    258         this.packageName = packageName;
    259 
    260         // Let the Secure class initialize the settings provider, which is done when we first
    261         // tries to get any setting.  Because our mock context/content resolver doesn't have the
    262         // settings provider, we need to do this with an actual context, before other classes
    263         // try to do this with a mock context.
    264         // (Otherwise ContactsProvider2.initialzie() will crash trying to get a setting with
    265         // a mock context.)
    266         android.provider.Settings.Secure.getString(overallContext.getContentResolver(), "dummy");
    267 
    268         RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(context,
    269                 overallContext, FILENAME_PREFIX);
    270         mProviderContext = new IsolatedContext(resolver, targetContextWrapper) {
    271             private final MockSharedPreferences mPrefs = new MockSharedPreferences();
    272 
    273             @Override
    274             public File getFilesDir() {
    275                 // TODO: Need to figure out something more graceful than this.
    276                 return new File("/data/data/com.android.providers.contacts.tests/files");
    277             }
    278 
    279             @Override
    280             public Object getSystemService(String name) {
    281                 if (Context.COUNTRY_DETECTOR.equals(name)) {
    282                     return mMockCountryDetector;
    283                 }
    284                 if (Context.ACCOUNT_SERVICE.equals(name)) {
    285                     return mMockAccountManager;
    286                 }
    287                 if (Context.USER_SERVICE.equals(name)) {
    288                     return mockUserManager;
    289                 }
    290                 // Use overallContext here; super.getSystemService() somehow won't return
    291                 // DevicePolicyManager.
    292                 return overallContext.getSystemService(name);
    293             }
    294 
    295             @Override
    296             public SharedPreferences getSharedPreferences(String name, int mode) {
    297                 return mPrefs;
    298             }
    299 
    300             @Override
    301             public int getUserId() {
    302                 return mockUserManager.getUserHandle();
    303             }
    304         };
    305 
    306         mMockAccountManager = new MockAccountManager(mProviderContext);
    307         mockUserManager = new MockUserManager(mProviderContext);
    308         provider = addProvider(providerClass, authority);
    309     }
    310 
    311     public Context getProviderContext() {
    312         return mProviderContext;
    313     }
    314 
    315     public void addAuthority(String authority) {
    316         resolver.addProvider(authority, provider);
    317     }
    318 
    319     public <T extends ContentProvider> T addProvider(Class<T> providerClass,
    320             String authority) throws Exception {
    321         return addProvider(providerClass, authority, mProviderContext);
    322     }
    323 
    324     public <T extends ContentProvider> T addProvider(Class<T> providerClass,
    325             String authority, Context providerContext) throws Exception {
    326         T provider = providerClass.newInstance();
    327         ProviderInfo info = new ProviderInfo();
    328 
    329         // Here, authority can have "user-id@".  We want to use it for addProvider, but provider
    330         // info shouldn't have it.
    331         info.authority = stripOutUserIdFromAuthority(authority);
    332         provider.attachInfoForTesting(providerContext, info);
    333         resolver.addProvider(authority, provider);
    334         return provider;
    335     }
    336 
    337     /**
    338      * Takes an provider authority. If it has "userid@", then remove it.
    339      */
    340     private String stripOutUserIdFromAuthority(String authority) {
    341         final int pos = authority.indexOf('@');
    342         return pos < 0 ? authority : authority.substring(pos + 1);
    343     }
    344 
    345     public void addPermissions(String... permissions) {
    346         mGrantedPermissions.addAll(Arrays.asList(permissions));
    347     }
    348 
    349     public void removePermissions(String... permissions) {
    350         mGrantedPermissions.removeAll(Arrays.asList(permissions));
    351     }
    352 
    353     public void addUriPermissions(Uri... uris) {
    354         mGrantedUriPermissions.addAll(Arrays.asList(uris));
    355     }
    356 
    357     public void removeUriPermissions(Uri... uris) {
    358         mGrantedUriPermissions.removeAll(Arrays.asList(uris));
    359     }
    360 
    361     /**
    362      * Mock {@link Context} that reports specific well-known values for testing
    363      * data protection. The creator can override the owner package name, and
    364      * force the {@link PackageManager} to always return a well-known package
    365      * list for any call to {@link PackageManager#getPackagesForUid(int)}.
    366      * <p>
    367      * For example, the creator could request that the {@link Context} lives in
    368      * package name "com.example.red", and also cause the {@link PackageManager}
    369      * to report that no UID contains that package name.
    370      */
    371     private static class RestrictionMockContext extends MockContext {
    372         private final Context mOverallContext;
    373         private final String mReportedPackageName;
    374         private final ContactsMockPackageManager mPackageManager;
    375         private final ContentResolver mResolver;
    376         private final Resources mRes;
    377         private final Set<String> mGrantedPermissions;
    378         private final Set<Uri> mGrantedUriPermissions;
    379 
    380         /**
    381          * Create a {@link Context} under the given package name.
    382          */
    383         public RestrictionMockContext(Context overallContext, String reportedPackageName,
    384                 ContentResolver resolver, Set<String> grantedPermissions,
    385                 Set<Uri> grantedUriPermissions) {
    386             mOverallContext = overallContext;
    387             mReportedPackageName = reportedPackageName;
    388             mResolver = resolver;
    389             mGrantedPermissions = grantedPermissions;
    390             mGrantedUriPermissions = grantedUriPermissions;
    391 
    392             mPackageManager = new ContactsMockPackageManager();
    393             mPackageManager.addPackage(1000, PACKAGE_GREY);
    394             mPackageManager.addPackage(2000, PACKAGE_RED);
    395             mPackageManager.addPackage(3000, PACKAGE_GREEN);
    396             mPackageManager.addPackage(4000, PACKAGE_BLUE);
    397 
    398             Resources resources = overallContext.getResources();
    399             Configuration configuration = new Configuration(resources.getConfiguration());
    400             configuration.locale = Locale.US;
    401             resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    402             mRes = resources;
    403         }
    404 
    405         @Override
    406         public String getPackageName() {
    407             return mReportedPackageName;
    408         }
    409 
    410         @Override
    411         public PackageManager getPackageManager() {
    412             return mPackageManager;
    413         }
    414 
    415         @Override
    416         public Resources getResources() {
    417             return mRes;
    418         }
    419 
    420         @Override
    421         public ContentResolver getContentResolver() {
    422             return mResolver;
    423         }
    424 
    425         @Override
    426         public ApplicationInfo getApplicationInfo() {
    427             ApplicationInfo ai = new ApplicationInfo();
    428             ai.packageName = "contactsTestPackage";
    429             return ai;
    430         }
    431 
    432         // All permission checks are implemented to simply check against the granted permission set.
    433 
    434         @Override
    435         public int checkPermission(String permission, int pid, int uid) {
    436             return checkCallingPermission(permission);
    437         }
    438 
    439         @Override
    440         public int checkCallingPermission(String permission) {
    441             if (mGrantedPermissions.contains(permission)) {
    442                 return PackageManager.PERMISSION_GRANTED;
    443             } else {
    444                 return PackageManager.PERMISSION_DENIED;
    445             }
    446         }
    447 
    448         @Override
    449         public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
    450             return checkCallingUriPermission(uri, modeFlags);
    451         }
    452 
    453         @Override
    454         public int checkCallingUriPermission(Uri uri, int modeFlags) {
    455             if (mGrantedUriPermissions.contains(uri)) {
    456                 return PackageManager.PERMISSION_GRANTED;
    457             } else {
    458                 return PackageManager.PERMISSION_DENIED;
    459             }
    460         }
    461 
    462         @Override
    463         public int checkCallingOrSelfPermission(String permission) {
    464             return checkCallingPermission(permission);
    465         }
    466 
    467         @Override
    468         public void enforcePermission(String permission, int pid, int uid, String message) {
    469             enforceCallingPermission(permission, message);
    470         }
    471 
    472         @Override
    473         public void enforceCallingPermission(String permission, String message) {
    474             if (!mGrantedPermissions.contains(permission)) {
    475                 throw new SecurityException(message);
    476             }
    477         }
    478 
    479         @Override
    480         public void enforceCallingOrSelfPermission(String permission, String message) {
    481             enforceCallingPermission(permission, message);
    482         }
    483 
    484         @Override
    485         public void sendBroadcast(Intent intent) {
    486             mOverallContext.sendBroadcast(intent);
    487         }
    488 
    489         @Override
    490         public void sendBroadcast(Intent intent, String receiverPermission) {
    491             mOverallContext.sendBroadcast(intent, receiverPermission);
    492         }
    493     }
    494 
    495     static String sCallingPackage = null;
    496 
    497     void ensureCallingPackage() {
    498         sCallingPackage = this.packageName;
    499     }
    500 
    501     public long createRawContact(String name) {
    502         ensureCallingPackage();
    503         long rawContactId = createRawContact();
    504         createName(rawContactId, name);
    505         return rawContactId;
    506     }
    507 
    508     public long createRawContact() {
    509         ensureCallingPackage();
    510         final ContentValues values = new ContentValues();
    511 
    512         Uri rawContactUri = resolver.insert(RawContacts.CONTENT_URI, values);
    513         return ContentUris.parseId(rawContactUri);
    514     }
    515 
    516     public long createRawContactWithStatus(String name, String address,
    517             String status) {
    518         final long rawContactId = createRawContact(name);
    519         final long dataId = createEmail(rawContactId, address);
    520         createStatus(dataId, status);
    521         return rawContactId;
    522     }
    523 
    524     public long createName(long contactId, String name) {
    525         ensureCallingPackage();
    526         final ContentValues values = new ContentValues();
    527         values.put(Data.RAW_CONTACT_ID, contactId);
    528         values.put(Data.IS_PRIMARY, 1);
    529         values.put(Data.IS_SUPER_PRIMARY, 1);
    530         values.put(Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
    531         values.put(CommonDataKinds.StructuredName.FAMILY_NAME, name);
    532         Uri insertUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
    533                 contactId), RawContacts.Data.CONTENT_DIRECTORY);
    534         Uri dataUri = resolver.insert(insertUri, values);
    535         return ContentUris.parseId(dataUri);
    536     }
    537 
    538     public long createPhone(long contactId, String phoneNumber) {
    539         ensureCallingPackage();
    540         final ContentValues values = new ContentValues();
    541         values.put(Data.RAW_CONTACT_ID, contactId);
    542         values.put(Data.IS_PRIMARY, 1);
    543         values.put(Data.IS_SUPER_PRIMARY, 1);
    544         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
    545         values.put(ContactsContract.CommonDataKinds.Phone.TYPE,
    546                 ContactsContract.CommonDataKinds.Phone.TYPE_HOME);
    547         values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber);
    548         Uri insertUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
    549                 contactId), RawContacts.Data.CONTENT_DIRECTORY);
    550         Uri dataUri = resolver.insert(insertUri, values);
    551         return ContentUris.parseId(dataUri);
    552     }
    553 
    554     public long createEmail(long contactId, String address) {
    555         ensureCallingPackage();
    556         final ContentValues values = new ContentValues();
    557         values.put(Data.RAW_CONTACT_ID, contactId);
    558         values.put(Data.IS_PRIMARY, 1);
    559         values.put(Data.IS_SUPER_PRIMARY, 1);
    560         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
    561         values.put(Email.TYPE, Email.TYPE_HOME);
    562         values.put(Email.DATA, address);
    563         Uri insertUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
    564                 contactId), RawContacts.Data.CONTENT_DIRECTORY);
    565         Uri dataUri = resolver.insert(insertUri, values);
    566         return ContentUris.parseId(dataUri);
    567     }
    568 
    569     public long createStatus(long dataId, String status) {
    570         ensureCallingPackage();
    571         final ContentValues values = new ContentValues();
    572         values.put(StatusUpdates.DATA_ID, dataId);
    573         values.put(StatusUpdates.STATUS, status);
    574         Uri dataUri = resolver.insert(StatusUpdates.CONTENT_URI, values);
    575         return ContentUris.parseId(dataUri);
    576     }
    577 
    578     public void updateException(String packageProvider, String packageClient, boolean allowAccess) {
    579         throw new UnsupportedOperationException("RestrictionExceptions are hard-coded");
    580     }
    581 
    582     public long getContactForRawContact(long rawContactId) {
    583         ensureCallingPackage();
    584         Uri contactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
    585         final Cursor cursor = resolver.query(contactUri, Projections.PROJ_RAW_CONTACTS, null,
    586                 null, null);
    587         if (!cursor.moveToFirst()) {
    588             cursor.close();
    589             throw new RuntimeException("Contact didn't have an aggregate");
    590         }
    591         final long aggId = cursor.getLong(Projections.COL_CONTACTS_ID);
    592         cursor.close();
    593         return aggId;
    594     }
    595 
    596     public int getDataCountForContact(long contactId) {
    597         ensureCallingPackage();
    598         Uri contactUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
    599                 contactId), Contacts.Data.CONTENT_DIRECTORY);
    600         final Cursor cursor = resolver.query(contactUri, Projections.PROJ_ID, null, null,
    601                 null);
    602         final int count = cursor.getCount();
    603         cursor.close();
    604         return count;
    605     }
    606 
    607     public int getDataCountForRawContact(long rawContactId) {
    608         ensureCallingPackage();
    609         Uri contactUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
    610                 rawContactId), Contacts.Data.CONTENT_DIRECTORY);
    611         final Cursor cursor = resolver.query(contactUri, Projections.PROJ_ID, null, null,
    612                 null);
    613         final int count = cursor.getCount();
    614         cursor.close();
    615         return count;
    616     }
    617 
    618     public void setSuperPrimaryPhone(long dataId) {
    619         ensureCallingPackage();
    620         final ContentValues values = new ContentValues();
    621         values.put(Data.IS_PRIMARY, 1);
    622         values.put(Data.IS_SUPER_PRIMARY, 1);
    623         Uri updateUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
    624         resolver.update(updateUri, values, null, null);
    625     }
    626 
    627     public long createGroup(String groupName) {
    628         ensureCallingPackage();
    629         final ContentValues values = new ContentValues();
    630         values.put(ContactsContract.Groups.RES_PACKAGE, packageName);
    631         values.put(ContactsContract.Groups.TITLE, groupName);
    632         Uri groupUri = resolver.insert(ContactsContract.Groups.CONTENT_URI, values);
    633         return ContentUris.parseId(groupUri);
    634     }
    635 
    636     public long createGroupMembership(long rawContactId, long groupId) {
    637         ensureCallingPackage();
    638         final ContentValues values = new ContentValues();
    639         values.put(Data.RAW_CONTACT_ID, rawContactId);
    640         values.put(Data.MIMETYPE, CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE);
    641         values.put(CommonDataKinds.GroupMembership.GROUP_ROW_ID, groupId);
    642         Uri insertUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
    643                 rawContactId), RawContacts.Data.CONTENT_DIRECTORY);
    644         Uri dataUri = resolver.insert(insertUri, values);
    645         return ContentUris.parseId(dataUri);
    646     }
    647 
    648     protected void setAggregationException(int type, long rawContactId1, long rawContactId2) {
    649         ContentValues values = new ContentValues();
    650         values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
    651         values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
    652         values.put(AggregationExceptions.TYPE, type);
    653         resolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
    654     }
    655 
    656     public void setAccounts(Account[] accounts) {
    657         mAccounts = accounts;
    658     }
    659 
    660     /**
    661      * Various internal database projections.
    662      */
    663     private interface Projections {
    664         static final String[] PROJ_ID = new String[] {
    665                 BaseColumns._ID,
    666         };
    667 
    668         static final int COL_ID = 0;
    669 
    670         static final String[] PROJ_RAW_CONTACTS = new String[] {
    671                 RawContacts.CONTACT_ID
    672         };
    673 
    674         static final int COL_CONTACTS_ID = 0;
    675     }
    676 }
    677