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