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