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