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