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