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