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