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