• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.contacts;
18 
19 import 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         @Override
isUserRunning(int userId)253         public boolean isUserRunning(int userId) {
254             return true;
255         }
256     }
257 
258     private MockTelephonyManager mMockTelephonyManager;
259 
260     private class MockTelephonyManager extends TelephonyManager {
MockTelephonyManager(Context context)261         public MockTelephonyManager(Context context) {
262             super(context);
263         }
264 
265         @Override
checkCarrierPrivilegesForPackageAnyPhone(String packageName)266         public int checkCarrierPrivilegesForPackageAnyPhone(String packageName) {
267             if (TextUtils.equals(packageName, ContactsActor.this.packageName)
268                     && mHasCarrierPrivileges) {
269                 return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
270             }
271             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
272         }
273 
274         @Override
getPackagesWithCarrierPrivileges()275         public List<String> getPackagesWithCarrierPrivileges() {
276             if (!mHasCarrierPrivileges) {
277                 return Collections.emptyList();
278             }
279             return Collections.singletonList(packageName);
280         }
281     }
282 
283     private TelecomManager mMockTelecomManager;
284 
285     /**
286      * A context wrapper that reports a different user id.
287      *
288      * TODO This should override getSystemService() and returns a UserManager that returns the
289      * same, altered user ID too.
290      */
291     public static class AlteringUserContext extends ContextWrapper {
292         private final int mUserId;
293 
AlteringUserContext(Context base, int userId)294         public AlteringUserContext(Context base, int userId) {
295             super(base);
296             mUserId = userId;
297         }
298 
299         @Override
getUserId()300         public int getUserId() {
301             return mUserId;
302         }
303     }
304 
305     private IsolatedContext mProviderContext;
306 
307     /**
308      * Create an "actor" using the given parent {@link Context} and the specific
309      * package name. Internally, all {@link Context} method calls are passed to
310      * a new instance of {@link RestrictionMockContext}, which stubs out the
311      * security infrastructure.
312      */
ContactsActor(final Context overallContext, String packageName, Class<? extends ContentProvider> providerClass, String authority)313     public ContactsActor(final Context overallContext, String packageName,
314             Class<? extends ContentProvider> providerClass, String authority) throws Exception {
315 
316         // Force permission check even when called by self.
317         ContactsPermissions.ALLOW_SELF_CALL = false;
318 
319         resolver = new MockContentResolver();
320         context = new RestrictionMockContext(overallContext, packageName, resolver,
321                 mGrantedPermissions, mGrantedUriPermissions) {
322             @Override
323             public Object getSystemService(String name) {
324                 if (Context.COUNTRY_DETECTOR.equals(name)) {
325                     return mMockCountryDetector;
326                 }
327                 if (Context.ACCOUNT_SERVICE.equals(name)) {
328                     return mMockAccountManager;
329                 }
330                 if (Context.USER_SERVICE.equals(name)) {
331                     return mockUserManager;
332                 }
333                 if (Context.TELEPHONY_SERVICE.equals(name)) {
334                     return mMockTelephonyManager;
335                 }
336                 if (Context.TELECOM_SERVICE.equals(name)) {
337                     return mMockTelecomManager;
338                 }
339                 // Use overallContext here; super.getSystemService() somehow won't return
340                 // DevicePolicyManager.
341                 return overallContext.getSystemService(name);
342             }
343 
344 
345 
346             @Override
347             public String getSystemServiceName(Class<?> serviceClass) {
348                 return overallContext.getSystemServiceName(serviceClass);
349             }
350         };
351         this.packageName = packageName;
352 
353         // Let the Secure class initialize the settings provider, which is done when we first
354         // tries to get any setting.  Because our mock context/content resolver doesn't have the
355         // settings provider, we need to do this with an actual context, before other classes
356         // try to do this with a mock context.
357         // (Otherwise ContactsProvider2.initialzie() will crash trying to get a setting with
358         // a mock context.)
359         android.provider.Settings.Secure.getString(overallContext.getContentResolver(), "dummy");
360 
361         RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(context,
362                 overallContext, FILENAME_PREFIX);
363         mProviderContext = new IsolatedContext(resolver, targetContextWrapper) {
364             private final MockSharedPreferences mPrefs = new MockSharedPreferences();
365 
366             @Override
367             public File getFilesDir() {
368                 // TODO: Need to figure out something more graceful than this.
369                 return new File("/data/data/com.android.providers.contacts.tests/files");
370             }
371 
372             @Override
373             public Object getSystemService(String name) {
374                 if (Context.COUNTRY_DETECTOR.equals(name)) {
375                     return mMockCountryDetector;
376                 }
377                 if (Context.ACCOUNT_SERVICE.equals(name)) {
378                     return mMockAccountManager;
379                 }
380                 if (Context.USER_SERVICE.equals(name)) {
381                     return mockUserManager;
382                 }
383                 if (Context.TELEPHONY_SERVICE.equals(name)) {
384                     return mMockTelephonyManager;
385                 }
386                 if (Context.TELECOM_SERVICE.equals(name)) {
387                     return mMockTelecomManager;
388                 }
389                 // Use overallContext here; super.getSystemService() somehow won't return
390                 // DevicePolicyManager.
391                 return overallContext.getSystemService(name);
392             }
393 
394             @Override
395             public String getSystemServiceName(Class<?> serviceClass) {
396                 return overallContext.getSystemServiceName(serviceClass);
397             }
398 
399             @Override
400             public SharedPreferences getSharedPreferences(String name, int mode) {
401                 return mPrefs;
402             }
403 
404             @Override
405             public int getUserId() {
406                 if (mockUserManager != null) {
407                     return mockUserManager.getUserHandle();
408                 } else {
409                     return DEFAULT_USER_ID;
410                 }
411             }
412 
413             @Override
414             public void sendBroadcast(Intent intent, String receiverPermission) {
415                 // Ignore.
416             }
417 
418             @Override
419             public Context getApplicationContext() {
420                 return this;
421             }
422         };
423 
424         mMockAccountManager = new MockAccountManager(mProviderContext);
425         mockUserManager = new MockUserManager(mProviderContext);
426         mMockTelephonyManager = new MockTelephonyManager(mProviderContext);
427         mMockTelecomManager = Mockito.mock(TelecomManager.class);
428         when(mMockTelecomManager.getDefaultDialerPackage()).thenReturn("");
429         when(mMockTelecomManager.getSystemDialerPackage()).thenReturn("");
430         provider = addProvider(providerClass, authority);
431     }
432 
getProviderContext()433     public Context getProviderContext() {
434         return mProviderContext;
435     }
436 
addProvider(Class<T> providerClass, String authority)437     public <T extends ContentProvider> T addProvider(Class<T> providerClass,
438             String authority) throws Exception {
439         return addProvider(providerClass, authority, mProviderContext);
440     }
441 
addProvider(Class<T> providerClass, String authority, Context providerContext)442     public <T extends ContentProvider> T addProvider(Class<T> providerClass,
443             String authority, Context providerContext) throws Exception {
444         return addProvider(providerClass.newInstance(), authority, providerContext);
445     }
446 
addProvider(T provider, String authority, Context providerContext)447     public <T extends ContentProvider> T addProvider(T provider,
448             String authority, Context providerContext) throws Exception {
449         ProviderInfo info = new ProviderInfo();
450 
451         // Here, authority can have "user-id@".  We want to use it for addProvider, but provider
452         // info shouldn't have it.
453         info.authority = stripOutUserIdFromAuthority(authority);
454         provider.attachInfoForTesting(providerContext, info);
455 
456         // In case of LegacyTest, "authority" here is actually multiple authorities.
457         // Register all authority here.
458         for (String a : authority.split(";")) {
459             resolver.addProvider(a, provider);
460             resolver.addProvider("0@" + a, provider);
461         }
462         mAllProviders.add(provider);
463         return provider;
464     }
465 
466     /**
467      * Takes an provider authority. If it has "userid@", then remove it.
468      */
stripOutUserIdFromAuthority(String authority)469     private String stripOutUserIdFromAuthority(String authority) {
470         final int pos = authority.indexOf('@');
471         return pos < 0 ? authority : authority.substring(pos + 1);
472     }
473 
addPermissions(String... permissions)474     public void addPermissions(String... permissions) {
475         mGrantedPermissions.addAll(Arrays.asList(permissions));
476     }
477 
removePermissions(String... permissions)478     public void removePermissions(String... permissions) {
479         mGrantedPermissions.removeAll(Arrays.asList(permissions));
480     }
481 
addUriPermissions(Uri... uris)482     public void addUriPermissions(Uri... uris) {
483         mGrantedUriPermissions.addAll(Arrays.asList(uris));
484     }
485 
removeUriPermissions(Uri... uris)486     public void removeUriPermissions(Uri... uris) {
487         mGrantedUriPermissions.removeAll(Arrays.asList(uris));
488     }
489 
grantCarrierPrivileges()490     public void grantCarrierPrivileges() {
491         mHasCarrierPrivileges = true;
492     }
493 
revokeCarrierPrivileges()494     public void revokeCarrierPrivileges() {
495         mHasCarrierPrivileges = false;
496     }
497 
498     /**
499      * Mock {@link Context} that reports specific well-known values for testing
500      * data protection. The creator can override the owner package name, and
501      * force the {@link PackageManager} to always return a well-known package
502      * list for any call to {@link PackageManager#getPackagesForUid(int)}.
503      * <p>
504      * For example, the creator could request that the {@link Context} lives in
505      * package name "com.example.red", and also cause the {@link PackageManager}
506      * to report that no UID contains that package name.
507      */
508     private static class RestrictionMockContext extends MockContext {
509         private final Context mOverallContext;
510         private final String mReportedPackageName;
511         private final ContactsMockPackageManager mPackageManager;
512         private final ContentResolver mResolver;
513         private final Resources mRes;
514         private final Set<String> mGrantedPermissions;
515         private final Set<Uri> mGrantedUriPermissions;
516 
517         /**
518          * Create a {@link Context} under the given package name.
519          */
RestrictionMockContext(Context overallContext, String reportedPackageName, ContentResolver resolver, Set<String> grantedPermissions, Set<Uri> grantedUriPermissions)520         public RestrictionMockContext(Context overallContext, String reportedPackageName,
521                 ContentResolver resolver, Set<String> grantedPermissions,
522                 Set<Uri> grantedUriPermissions) {
523             mOverallContext = overallContext;
524             mReportedPackageName = reportedPackageName;
525             mResolver = resolver;
526             mGrantedPermissions = grantedPermissions;
527             mGrantedUriPermissions = grantedUriPermissions;
528 
529             mPackageManager = new ContactsMockPackageManager(overallContext);
530             mPackageManager.addPackage(1000, PACKAGE_GREY);
531             mPackageManager.addPackage(2000, PACKAGE_RED);
532             mPackageManager.addPackage(3000, PACKAGE_GREEN);
533             mPackageManager.addPackage(4000, PACKAGE_BLUE);
534 
535             Resources resources = overallContext.getResources();
536             Configuration configuration = new Configuration(resources.getConfiguration());
537             configuration.locale = Locale.US;
538             resources.updateConfiguration(configuration, resources.getDisplayMetrics());
539             mRes = resources;
540         }
541 
542         @Override
getPackageName()543         public String getPackageName() {
544             return mReportedPackageName;
545         }
546 
547         @Override
getPackageManager()548         public PackageManager getPackageManager() {
549             return mPackageManager;
550         }
551 
552         @Override
getResources()553         public Resources getResources() {
554             return mRes;
555         }
556 
557         @Override
getContentResolver()558         public ContentResolver getContentResolver() {
559             return mResolver;
560         }
561 
562         @Override
getApplicationInfo()563         public ApplicationInfo getApplicationInfo() {
564             ApplicationInfo ai = new ApplicationInfo();
565             ai.packageName = "contactsTestPackage";
566             return ai;
567         }
568 
569         // All permission checks are implemented to simply check against the granted permission set.
570 
571         @Override
checkPermission(String permission, int pid, int uid)572         public int checkPermission(String permission, int pid, int uid) {
573             return checkCallingPermission(permission);
574         }
575 
576         @Override
checkCallingPermission(String permission)577         public int checkCallingPermission(String permission) {
578             if (mGrantedPermissions.contains(permission)) {
579                 return PackageManager.PERMISSION_GRANTED;
580             } else {
581                 return PackageManager.PERMISSION_DENIED;
582             }
583         }
584 
585         @Override
checkUriPermission(Uri uri, int pid, int uid, int modeFlags)586         public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
587             return checkCallingUriPermission(uri, modeFlags);
588         }
589 
590         @Override
checkCallingUriPermission(Uri uri, int modeFlags)591         public int checkCallingUriPermission(Uri uri, int modeFlags) {
592             if (mGrantedUriPermissions.contains(uri)) {
593                 return PackageManager.PERMISSION_GRANTED;
594             } else {
595                 return PackageManager.PERMISSION_DENIED;
596             }
597         }
598 
599         @Override
checkCallingOrSelfPermission(String permission)600         public int checkCallingOrSelfPermission(String permission) {
601             return checkCallingPermission(permission);
602         }
603 
604         @Override
enforcePermission(String permission, int pid, int uid, String message)605         public void enforcePermission(String permission, int pid, int uid, String message) {
606             enforceCallingPermission(permission, message);
607         }
608 
609         @Override
enforceCallingPermission(String permission, String message)610         public void enforceCallingPermission(String permission, String message) {
611             if (!mGrantedPermissions.contains(permission)) {
612                 throw new SecurityException(message);
613             }
614         }
615 
616         @Override
enforceCallingOrSelfPermission(String permission, String message)617         public void enforceCallingOrSelfPermission(String permission, String message) {
618             enforceCallingPermission(permission, message);
619         }
620 
621         @Override
sendBroadcast(Intent intent)622         public void sendBroadcast(Intent intent) {
623             mOverallContext.sendBroadcast(intent);
624         }
625 
626         @Override
sendBroadcast(Intent intent, String receiverPermission)627         public void sendBroadcast(Intent intent, String receiverPermission) {
628             mOverallContext.sendBroadcast(intent, receiverPermission);
629         }
630     }
631 
632     static String sCallingPackage = null;
633 
ensureCallingPackage()634     void ensureCallingPackage() {
635         sCallingPackage = this.packageName;
636     }
637 
createRawContact(String name)638     public long createRawContact(String name) {
639         ensureCallingPackage();
640         long rawContactId = createRawContact();
641         createName(rawContactId, name);
642         return rawContactId;
643     }
644 
createRawContact()645     public long createRawContact() {
646         ensureCallingPackage();
647         final ContentValues values = new ContentValues();
648 
649         Uri rawContactUri = resolver.insert(RawContacts.CONTENT_URI, values);
650         return ContentUris.parseId(rawContactUri);
651     }
652 
createRawContactWithStatus(String name, String address, String status)653     public long createRawContactWithStatus(String name, String address,
654             String status) {
655         final long rawContactId = createRawContact(name);
656         final long dataId = createEmail(rawContactId, address);
657         createStatus(dataId, status);
658         return rawContactId;
659     }
660 
createName(long contactId, String name)661     public long createName(long contactId, String name) {
662         ensureCallingPackage();
663         final ContentValues values = new ContentValues();
664         values.put(Data.RAW_CONTACT_ID, contactId);
665         values.put(Data.IS_PRIMARY, 1);
666         values.put(Data.IS_SUPER_PRIMARY, 1);
667         values.put(Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
668         values.put(CommonDataKinds.StructuredName.FAMILY_NAME, name);
669         Uri insertUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
670                 contactId), RawContacts.Data.CONTENT_DIRECTORY);
671         Uri dataUri = resolver.insert(insertUri, values);
672         return ContentUris.parseId(dataUri);
673     }
674 
createPhone(long contactId, String phoneNumber)675     public long createPhone(long contactId, String phoneNumber) {
676         ensureCallingPackage();
677         final ContentValues values = new ContentValues();
678         values.put(Data.RAW_CONTACT_ID, contactId);
679         values.put(Data.IS_PRIMARY, 1);
680         values.put(Data.IS_SUPER_PRIMARY, 1);
681         values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
682         values.put(ContactsContract.CommonDataKinds.Phone.TYPE,
683                 ContactsContract.CommonDataKinds.Phone.TYPE_HOME);
684         values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber);
685         Uri insertUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
686                 contactId), RawContacts.Data.CONTENT_DIRECTORY);
687         Uri dataUri = resolver.insert(insertUri, values);
688         return ContentUris.parseId(dataUri);
689     }
690 
createEmail(long contactId, String address)691     public long createEmail(long contactId, String address) {
692         ensureCallingPackage();
693         final ContentValues values = new ContentValues();
694         values.put(Data.RAW_CONTACT_ID, contactId);
695         values.put(Data.IS_PRIMARY, 1);
696         values.put(Data.IS_SUPER_PRIMARY, 1);
697         values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
698         values.put(Email.TYPE, Email.TYPE_HOME);
699         values.put(Email.DATA, address);
700         Uri insertUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
701                 contactId), RawContacts.Data.CONTENT_DIRECTORY);
702         Uri dataUri = resolver.insert(insertUri, values);
703         return ContentUris.parseId(dataUri);
704     }
705 
createStatus(long dataId, String status)706     public long createStatus(long dataId, String status) {
707         ensureCallingPackage();
708         final ContentValues values = new ContentValues();
709         values.put(StatusUpdates.DATA_ID, dataId);
710         values.put(StatusUpdates.STATUS, status);
711         Uri dataUri = resolver.insert(StatusUpdates.CONTENT_URI, values);
712         return ContentUris.parseId(dataUri);
713     }
714 
updateException(String packageProvider, String packageClient, boolean allowAccess)715     public void updateException(String packageProvider, String packageClient, boolean allowAccess) {
716         throw new UnsupportedOperationException("RestrictionExceptions are hard-coded");
717     }
718 
getContactForRawContact(long rawContactId)719     public long getContactForRawContact(long rawContactId) {
720         ensureCallingPackage();
721         Uri contactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
722         final Cursor cursor = resolver.query(contactUri, Projections.PROJ_RAW_CONTACTS, null,
723                 null, null);
724         if (!cursor.moveToFirst()) {
725             cursor.close();
726             throw new RuntimeException("Contact didn't have an aggregate");
727         }
728         final long aggId = cursor.getLong(Projections.COL_CONTACTS_ID);
729         cursor.close();
730         return aggId;
731     }
732 
getDataCountForContact(long contactId)733     public int getDataCountForContact(long contactId) {
734         ensureCallingPackage();
735         Uri contactUri = Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI,
736                 contactId), Contacts.Data.CONTENT_DIRECTORY);
737         final Cursor cursor = resolver.query(contactUri, Projections.PROJ_ID, null, null,
738                 null);
739         final int count = cursor.getCount();
740         cursor.close();
741         return count;
742     }
743 
getDataCountForRawContact(long rawContactId)744     public int getDataCountForRawContact(long rawContactId) {
745         ensureCallingPackage();
746         Uri contactUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
747                 rawContactId), Contacts.Data.CONTENT_DIRECTORY);
748         final Cursor cursor = resolver.query(contactUri, Projections.PROJ_ID, null, null,
749                 null);
750         final int count = cursor.getCount();
751         cursor.close();
752         return count;
753     }
754 
setSuperPrimaryPhone(long dataId)755     public void setSuperPrimaryPhone(long dataId) {
756         ensureCallingPackage();
757         final ContentValues values = new ContentValues();
758         values.put(Data.IS_PRIMARY, 1);
759         values.put(Data.IS_SUPER_PRIMARY, 1);
760         Uri updateUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
761         resolver.update(updateUri, values, null, null);
762     }
763 
createGroup(String groupName)764     public long createGroup(String groupName) {
765         ensureCallingPackage();
766         final ContentValues values = new ContentValues();
767         values.put(ContactsContract.Groups.RES_PACKAGE, packageName);
768         values.put(ContactsContract.Groups.TITLE, groupName);
769         Uri groupUri = resolver.insert(ContactsContract.Groups.CONTENT_URI, values);
770         return ContentUris.parseId(groupUri);
771     }
772 
createGroupMembership(long rawContactId, long groupId)773     public long createGroupMembership(long rawContactId, long groupId) {
774         ensureCallingPackage();
775         final ContentValues values = new ContentValues();
776         values.put(Data.RAW_CONTACT_ID, rawContactId);
777         values.put(Data.MIMETYPE, CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE);
778         values.put(CommonDataKinds.GroupMembership.GROUP_ROW_ID, groupId);
779         Uri insertUri = Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI,
780                 rawContactId), RawContacts.Data.CONTENT_DIRECTORY);
781         Uri dataUri = resolver.insert(insertUri, values);
782         return ContentUris.parseId(dataUri);
783     }
784 
setAggregationException(int type, long rawContactId1, long rawContactId2)785     protected void setAggregationException(int type, long rawContactId1, long rawContactId2) {
786         ContentValues values = new ContentValues();
787         values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
788         values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
789         values.put(AggregationExceptions.TYPE, type);
790         resolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
791     }
792 
setAccounts(Account[] accounts)793     public void setAccounts(Account[] accounts) {
794         mAccounts = accounts;
795     }
796 
797     /**
798      * Various internal database projections.
799      */
800     private interface Projections {
801         static final String[] PROJ_ID = new String[] {
802                 BaseColumns._ID,
803         };
804 
805         static final int COL_ID = 0;
806 
807         static final String[] PROJ_RAW_CONTACTS = new String[] {
808                 RawContacts.CONTACT_ID
809         };
810 
811         static final int COL_CONTACTS_ID = 0;
812     }
813 
shutdown()814     public void shutdown() {
815         for (ContentProvider provider : mAllProviders) {
816             provider.shutdown();
817         }
818     }
819 }
820