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