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