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