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