• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
MockAccountManager(Context conteact)122         public MockAccountManager(Context conteact) {
123             super(context, null, null);
124         }
125 
126         @Override
addOnAccountsUpdatedListener(OnAccountsUpdateListener listener, Handler handler, boolean updateImmediately)127         public void addOnAccountsUpdatedListener(OnAccountsUpdateListener listener,
128                 Handler handler, boolean updateImmediately) {
129             // do nothing
130         }
131 
132         @Override
getAccounts()133         public Account[] getAccounts() {
134             return mAccounts;
135         }
136 
137         @Override
getAccountsByTypeAndFeatures( final String type, final String[] features, AccountManagerCallback<Account[]> callback, Handler handler)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
blockingGetAuthToken(Account account, String authTokenType, boolean notifyAuthFailure)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 {
createUserInfo(String name, int id, int groupId, int flags)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 
MockUserManager(Context context)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. */
setUsers(UserInfo... users)182         public void setUsers(UserInfo... users) {
183             mUsers.clear();
184             for (UserInfo ui : users) {
185                 mUsers.add(ui);
186             }
187         }
188 
189         @Override
getUserHandle()190         public int getUserHandle() {
191             return myUser;
192         }
193 
194         @Override
getUserInfo(int userHandle)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
getProfileParent(int userHandle)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
getUsers()219         public List<UserInfo> getUsers() {
220             return mUsers;
221         }
222 
223         @Override
getUserRestrictions(UserHandle userHandle)224         public Bundle getUserRestrictions(UserHandle userHandle) {
225             return new Bundle();
226         }
227 
228         @Override
hasUserRestriction(String restrictionKey)229         public boolean hasUserRestriction(String restrictionKey) {
230             return false;
231         }
232 
233         @Override
hasUserRestriction(String restrictionKey, UserHandle userHandle)234         public boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
235             return false;
236         }
237 
238         @Override
isSameProfileGroup(int userId, int otherUserId)239         public boolean isSameProfileGroup(int userId, int otherUserId) {
240             return getUserInfo(userId).profileGroupId == getUserInfo(otherUserId).profileGroupId;
241         }
242 
243         @Override
isUserUnlocked(int userId)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 {
MockTelephonyManager(Context context)252         public MockTelephonyManager(Context context) {
253             super(context);
254         }
255 
256         @Override
checkCarrierPrivilegesForPackageAnyPhone(String packageName)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
getPackagesWithCarrierPrivileges()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 
AlteringUserContext(Context base, int userId)283         public AlteringUserContext(Context base, int userId) {
284             super(base);
285             mUserId = userId;
286         }
287 
288         @Override
getUserId()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      */
ContactsActor(final Context overallContext, String packageName, Class<? extends ContentProvider> providerClass, String authority)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 
getProviderContext()409     public Context getProviderContext() {
410         return mProviderContext;
411     }
412 
addProvider(Class<T> providerClass, String authority)413     public <T extends ContentProvider> T addProvider(Class<T> providerClass,
414             String authority) throws Exception {
415         return addProvider(providerClass, authority, mProviderContext);
416     }
417 
addProvider(Class<T> providerClass, String authority, Context providerContext)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 
addProvider(T provider, String authority, Context providerContext)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      */
stripOutUserIdFromAuthority(String authority)445     private String stripOutUserIdFromAuthority(String authority) {
446         final int pos = authority.indexOf('@');
447         return pos < 0 ? authority : authority.substring(pos + 1);
448     }
449 
addPermissions(String... permissions)450     public void addPermissions(String... permissions) {
451         mGrantedPermissions.addAll(Arrays.asList(permissions));
452     }
453 
removePermissions(String... permissions)454     public void removePermissions(String... permissions) {
455         mGrantedPermissions.removeAll(Arrays.asList(permissions));
456     }
457 
addUriPermissions(Uri... uris)458     public void addUriPermissions(Uri... uris) {
459         mGrantedUriPermissions.addAll(Arrays.asList(uris));
460     }
461 
removeUriPermissions(Uri... uris)462     public void removeUriPermissions(Uri... uris) {
463         mGrantedUriPermissions.removeAll(Arrays.asList(uris));
464     }
465 
grantCarrierPrivileges()466     public void grantCarrierPrivileges() {
467         mHasCarrierPrivileges = true;
468     }
469 
revokeCarrierPrivileges()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          */
RestrictionMockContext(Context overallContext, String reportedPackageName, ContentResolver resolver, Set<String> grantedPermissions, Set<Uri> grantedUriPermissions)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
getPackageName()519         public String getPackageName() {
520             return mReportedPackageName;
521         }
522 
523         @Override
getPackageManager()524         public PackageManager getPackageManager() {
525             return mPackageManager;
526         }
527 
528         @Override
getResources()529         public Resources getResources() {
530             return mRes;
531         }
532 
533         @Override
getContentResolver()534         public ContentResolver getContentResolver() {
535             return mResolver;
536         }
537 
538         @Override
getApplicationInfo()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
checkPermission(String permission, int pid, int uid)548         public int checkPermission(String permission, int pid, int uid) {
549             return checkCallingPermission(permission);
550         }
551 
552         @Override
checkCallingPermission(String permission)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
checkUriPermission(Uri uri, int pid, int uid, int modeFlags)562         public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
563             return checkCallingUriPermission(uri, modeFlags);
564         }
565 
566         @Override
checkCallingUriPermission(Uri uri, int modeFlags)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
checkCallingOrSelfPermission(String permission)576         public int checkCallingOrSelfPermission(String permission) {
577             return checkCallingPermission(permission);
578         }
579 
580         @Override
enforcePermission(String permission, int pid, int uid, String message)581         public void enforcePermission(String permission, int pid, int uid, String message) {
582             enforceCallingPermission(permission, message);
583         }
584 
585         @Override
enforceCallingPermission(String permission, String message)586         public void enforceCallingPermission(String permission, String message) {
587             if (!mGrantedPermissions.contains(permission)) {
588                 throw new SecurityException(message);
589             }
590         }
591 
592         @Override
enforceCallingOrSelfPermission(String permission, String message)593         public void enforceCallingOrSelfPermission(String permission, String message) {
594             enforceCallingPermission(permission, message);
595         }
596 
597         @Override
sendBroadcast(Intent intent)598         public void sendBroadcast(Intent intent) {
599             mOverallContext.sendBroadcast(intent);
600         }
601 
602         @Override
sendBroadcast(Intent intent, String receiverPermission)603         public void sendBroadcast(Intent intent, String receiverPermission) {
604             mOverallContext.sendBroadcast(intent, receiverPermission);
605         }
606     }
607 
608     static String sCallingPackage = null;
609 
ensureCallingPackage()610     void ensureCallingPackage() {
611         sCallingPackage = this.packageName;
612     }
613 
createRawContact(String name)614     public long createRawContact(String name) {
615         ensureCallingPackage();
616         long rawContactId = createRawContact();
617         createName(rawContactId, name);
618         return rawContactId;
619     }
620 
createRawContact()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 
createRawContactWithStatus(String name, String address, String status)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 
createName(long contactId, String name)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 
createPhone(long contactId, String phoneNumber)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 
createEmail(long contactId, String address)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 
createStatus(long dataId, String status)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 
updateException(String packageProvider, String packageClient, boolean allowAccess)691     public void updateException(String packageProvider, String packageClient, boolean allowAccess) {
692         throw new UnsupportedOperationException("RestrictionExceptions are hard-coded");
693     }
694 
getContactForRawContact(long rawContactId)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 
getDataCountForContact(long contactId)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 
getDataCountForRawContact(long rawContactId)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 
setSuperPrimaryPhone(long dataId)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 
createGroup(String groupName)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 
createGroupMembership(long rawContactId, long groupId)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 
setAggregationException(int type, long rawContactId1, long rawContactId2)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 
setAccounts(Account[] accounts)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 
shutdown()790     public void shutdown() {
791         for (ContentProvider provider : mAllProviders) {
792             provider.shutdown();
793         }
794     }
795 }
796