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