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