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