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