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