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