1 /* 2 * Copyright (C) 2010 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.annotation.NonNull; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.Flags; 24 import android.content.pm.PackageInfo; 25 import android.content.pm.PackageManager; 26 import android.content.pm.PackageManager.NameNotFoundException; 27 import android.content.pm.ProviderInfo; 28 import android.content.res.Resources; 29 import android.content.res.Resources.NotFoundException; 30 import android.database.Cursor; 31 import android.database.sqlite.SQLiteDatabase; 32 import android.net.Uri; 33 import android.os.Bundle; 34 import android.os.SystemClock; 35 import android.provider.ContactsContract; 36 import android.provider.ContactsContract.Directory; 37 import android.sysprop.ContactsProperties; 38 import android.text.TextUtils; 39 import android.util.Log; 40 41 import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties; 42 import com.android.providers.contacts.ContactsDatabaseHelper.DirectoryColumns; 43 import com.android.providers.contacts.ContactsDatabaseHelper.Tables; 44 45 import com.google.android.collect.Lists; 46 import com.google.android.collect.Sets; 47 import com.google.common.annotations.VisibleForTesting; 48 49 import java.util.ArrayList; 50 import java.util.List; 51 import java.util.Objects; 52 import java.util.Set; 53 54 /** 55 * Manages the contents of the {@link Directory} table. 56 */ 57 public class ContactDirectoryManager { 58 59 private static final String TAG = "ContactDirectoryManager"; 60 private static final boolean DEBUG = AbstractContactsProvider.VERBOSE_LOGGING; 61 62 public static final String CONTACT_DIRECTORY_META_DATA = "android.content.ContactDirectory"; 63 64 public static class DirectoryInfo { 65 long id; 66 String packageName; 67 String authority; 68 String accountName; 69 String accountType; 70 String displayName; 71 int typeResourceId; 72 int exportSupport = Directory.EXPORT_SUPPORT_NONE; 73 int shortcutSupport = Directory.SHORTCUT_SUPPORT_NONE; 74 int photoSupport = Directory.PHOTO_SUPPORT_NONE; 75 @Override toString()76 public String toString() { 77 return "DirectoryInfo:" 78 + "id=" + id 79 + " packageName=" + accountType 80 + " authority=" + authority 81 + " accountName=***" 82 + " accountType=" + accountType; 83 } 84 } 85 86 private final static class DirectoryQuery { 87 public static final String[] PROJECTION = { 88 Directory.ACCOUNT_NAME, 89 Directory.ACCOUNT_TYPE, 90 Directory.DISPLAY_NAME, 91 Directory.TYPE_RESOURCE_ID, 92 Directory.EXPORT_SUPPORT, 93 Directory.SHORTCUT_SUPPORT, 94 Directory.PHOTO_SUPPORT, 95 }; 96 97 public static final int ACCOUNT_NAME = 0; 98 public static final int ACCOUNT_TYPE = 1; 99 public static final int DISPLAY_NAME = 2; 100 public static final int TYPE_RESOURCE_ID = 3; 101 public static final int EXPORT_SUPPORT = 4; 102 public static final int SHORTCUT_SUPPORT = 5; 103 public static final int PHOTO_SUPPORT = 6; 104 } 105 106 private final ContactsProvider2 mContactsProvider; 107 private final Context mContext; 108 private final PackageManager mPackageManager; 109 110 private volatile boolean mDirectoriesForceUpdated = false; 111 ContactDirectoryManager(ContactsProvider2 contactsProvider)112 public ContactDirectoryManager(ContactsProvider2 contactsProvider) { 113 mContactsProvider = contactsProvider; 114 mContext = contactsProvider.getContext(); 115 mPackageManager = mContext.getPackageManager(); 116 } 117 getDbHelper()118 public ContactsDatabaseHelper getDbHelper() { 119 return (ContactsDatabaseHelper) mContactsProvider.getDatabaseHelper(); 120 } 121 setDirectoriesForceUpdated(boolean updated)122 public void setDirectoriesForceUpdated(boolean updated) { 123 mDirectoriesForceUpdated = updated; 124 } 125 126 /** 127 * Scans through existing directories to see if the cached resource IDs still 128 * match their original resource names. If not - plays it safe by refreshing all directories. 129 * 130 * @return true if all resource IDs were found valid 131 */ areTypeResourceIdsValid()132 private boolean areTypeResourceIdsValid() { 133 SQLiteDatabase db = getDbHelper().getReadableDatabase(); 134 135 final Cursor cursor = db.rawQuery("SELECT DISTINCT " 136 + Directory.TYPE_RESOURCE_ID + "," 137 + Directory.PACKAGE_NAME + "," 138 + DirectoryColumns.TYPE_RESOURCE_NAME 139 + " FROM " + Tables.DIRECTORIES, null); 140 try { 141 while (cursor.moveToNext()) { 142 int resourceId = cursor.getInt(0); 143 if (resourceId != 0) { 144 String packageName = cursor.getString(1); 145 String storedResourceName = cursor.getString(2); 146 String resourceName = getResourceNameById(packageName, resourceId); 147 if (!TextUtils.equals(storedResourceName, resourceName)) { 148 if (DEBUG) { 149 Log.d(TAG, "areTypeResourceIdsValid:" 150 + " resourceId=" + resourceId 151 + " packageName=" + packageName 152 + " storedResourceName=" + storedResourceName 153 + " resourceName=" + resourceName); 154 } 155 return false; 156 } 157 } 158 } 159 } finally { 160 cursor.close(); 161 } 162 163 return true; 164 } 165 166 /** 167 * Given a resource ID, returns the corresponding resource name or null if the package name / 168 * resource ID combination is invalid. 169 */ getResourceNameById(String packageName, int resourceId)170 private String getResourceNameById(String packageName, int resourceId) { 171 try { 172 Resources resources = mPackageManager.getResourcesForApplication(packageName); 173 return resources.getResourceName(resourceId); 174 } catch (NameNotFoundException e) { 175 return null; 176 } catch (NotFoundException e) { 177 return null; 178 } 179 } 180 saveKnownDirectoryProviders(Set<String> packages)181 private void saveKnownDirectoryProviders(Set<String> packages) { 182 getDbHelper().setProperty(DbProperties.KNOWN_DIRECTORY_PACKAGES, 183 TextUtils.join(",", packages)); 184 } 185 haveKnownDirectoryProvidersChanged(Set<String> packages)186 private boolean haveKnownDirectoryProvidersChanged(Set<String> packages) { 187 final String directoryPackages = TextUtils.join(",", packages); 188 final String prev = getDbHelper().getProperty(DbProperties.KNOWN_DIRECTORY_PACKAGES, ""); 189 190 final boolean changed = !Objects.equals(directoryPackages, prev); 191 if (DEBUG) { 192 Log.d(TAG, "haveKnownDirectoryProvidersChanged=" + changed + "\nprev=" + prev 193 + " current=" + directoryPackages); 194 } 195 return changed; 196 } 197 198 @VisibleForTesting isRescanNeeded()199 boolean isRescanNeeded() { 200 if (ContactsProperties.debug_scan_all_packages().orElse(false)) { 201 Log.w(TAG, "debug.cp2.scan_all_packages set to 1."); 202 return true; // For debugging. 203 } 204 final String scanComplete = 205 getDbHelper().getProperty(DbProperties.DIRECTORY_SCAN_COMPLETE, "0"); 206 if (!"1".equals(scanComplete)) { 207 if (DEBUG) { 208 Log.d(TAG, "DIRECTORY_SCAN_COMPLETE is 0."); 209 } 210 return true; 211 } 212 if (haveKnownDirectoryProvidersChanged(getDirectoryProviderPackages(mPackageManager))) { 213 Log.i(TAG, "Directory provider packages have changed."); 214 return true; 215 } 216 return false; 217 } 218 219 /** 220 * Scans all packages for directory content providers. 221 */ scanAllPackages(boolean rescan)222 public int scanAllPackages(boolean rescan) { 223 if (!areTypeResourceIdsValid()) { 224 rescan = true; 225 Log.i(TAG, "!areTypeResourceIdsValid."); 226 } 227 if (rescan) { 228 getDbHelper().forceDirectoryRescan(); 229 } 230 231 return scanAllPackagesIfNeeded(); 232 } 233 scanAllPackagesIfNeeded()234 private int scanAllPackagesIfNeeded() { 235 if (!isRescanNeeded()) { 236 return 0; 237 } 238 if (DEBUG) { 239 Log.d(TAG, "scanAllPackagesIfNeeded()"); 240 } 241 final long start = SystemClock.elapsedRealtime(); 242 // Reset directory updated flag to false. If it's changed to true 243 // then we need to rescan directories. 244 mDirectoriesForceUpdated = false; 245 final int count = scanAllPackages(); 246 getDbHelper().setProperty(DbProperties.DIRECTORY_SCAN_COMPLETE, "1"); 247 final long end = SystemClock.elapsedRealtime(); 248 Log.i(TAG, "Discovered " + count + " contact directories in " + (end - start) + "ms"); 249 250 // Announce the change to listeners of the contacts authority 251 mContactsProvider.notifyChange(/* syncToNetwork =*/false); 252 253 // We schedule a rescan if update(DIRECTORIES) is called while we're scanning all packages. 254 if (mDirectoriesForceUpdated) { 255 mDirectoriesForceUpdated = false; 256 mContactsProvider.scheduleRescanDirectories(); 257 } 258 259 return count; 260 } 261 262 @VisibleForTesting isDirectoryProvider(ProviderInfo provider)263 static boolean isDirectoryProvider(ProviderInfo provider) { 264 if (provider == null) return false; 265 Bundle metaData = provider.metaData; 266 if (metaData == null) return false; 267 268 Object trueFalse = metaData.get(CONTACT_DIRECTORY_META_DATA); 269 return trueFalse != null && Boolean.TRUE.equals(trueFalse); 270 } 271 272 @NonNull getDirectoryProviderInfos(PackageManager pm)273 static private List<ProviderInfo> getDirectoryProviderInfos(PackageManager pm) { 274 return pm.queryContentProviders(null, 0, 0, CONTACT_DIRECTORY_META_DATA); 275 } 276 277 /** 278 * @return List of packages that contain a directory provider. 279 */ 280 @VisibleForTesting 281 @NonNull getDirectoryProviderPackages(PackageManager pm)282 static Set<String> getDirectoryProviderPackages(PackageManager pm) { 283 final Set<String> ret = Sets.newHashSet(); 284 285 if (DEBUG) { 286 Log.d(TAG, "Listing directory provider packages..."); 287 } 288 289 for (ProviderInfo provider : getDirectoryProviderInfos(pm)) { 290 ret.add(provider.packageName); 291 } 292 if (DEBUG) { 293 Log.d(TAG, "Found " + ret.size() + " directory provider packages"); 294 } 295 296 return ret; 297 } 298 scanAllPackages()299 private int scanAllPackages() { 300 SQLiteDatabase db = getDbHelper().getWritableDatabase(); 301 insertDefaultDirectory(db); 302 insertLocalInvisibleDirectory(db); 303 304 int count = 0; 305 306 // Prepare query strings for removing stale rows which don't correspond to existing 307 // directories. 308 StringBuilder deleteWhereBuilder = new StringBuilder(); 309 ArrayList<String> deleteWhereArgs = new ArrayList<String>(); 310 deleteWhereBuilder.append("NOT (" + Directory._ID + "=? OR " + Directory._ID + "=?"); 311 deleteWhereArgs.add(String.valueOf(Directory.DEFAULT)); 312 deleteWhereArgs.add(String.valueOf(Directory.LOCAL_INVISIBLE)); 313 final String wherePart = "(" + Directory.PACKAGE_NAME + "=? AND " 314 + Directory.DIRECTORY_AUTHORITY + "=? AND " 315 + Directory.ACCOUNT_NAME + "=? AND " 316 + Directory.ACCOUNT_TYPE + "=?)"; 317 318 final Set<String> directoryProviderPackages = getDirectoryProviderPackages(mPackageManager); 319 for (String packageName : directoryProviderPackages) { 320 if (DEBUG) Log.d(TAG, "package=" + packageName); 321 322 // getDirectoryProviderPackages() shouldn't return the contacts provider package 323 // because it doesn't have CONTACT_DIRECTORY_META_DATA, but just to make sure... 324 if (mContext.getPackageName().equals(packageName)) { 325 Log.w(TAG, " skipping self"); 326 continue; 327 } 328 329 final PackageInfo packageInfo; 330 try { 331 packageInfo = mPackageManager.getPackageInfo(packageName, 332 PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA); 333 if (packageInfo == null) continue; // Just in case... 334 } catch (NameNotFoundException nnfe) { 335 continue; // Application just removed? 336 } 337 338 List<DirectoryInfo> directories = updateDirectoriesForPackage(packageInfo, true); 339 if (directories != null && !directories.isEmpty()) { 340 count += directories.size(); 341 342 // We shouldn't delete rows for existing directories. 343 for (DirectoryInfo info : directories) { 344 if (DEBUG) Log.d(TAG, " directory=" + info); 345 deleteWhereBuilder.append(" OR "); 346 deleteWhereBuilder.append(wherePart); 347 deleteWhereArgs.add(info.packageName); 348 deleteWhereArgs.add(info.authority); 349 deleteWhereArgs.add(info.accountName); 350 deleteWhereArgs.add(info.accountType); 351 } 352 } 353 } 354 355 deleteWhereBuilder.append(")"); // Close "NOT (" 356 357 int deletedRows = db.delete(Tables.DIRECTORIES, deleteWhereBuilder.toString(), 358 deleteWhereArgs.toArray(new String[0])); 359 360 saveKnownDirectoryProviders(directoryProviderPackages); 361 362 Log.i(TAG, "deleted " + deletedRows 363 + " stale rows which don't have any relevant directory"); 364 return count; 365 } 366 insertDefaultDirectory(SQLiteDatabase db)367 private void insertDefaultDirectory(SQLiteDatabase db) { 368 ContentValues values = new ContentValues(); 369 values.put(Directory._ID, Directory.DEFAULT); 370 values.put(Directory.PACKAGE_NAME, mContext.getPackageName()); 371 values.put(Directory.DIRECTORY_AUTHORITY, ContactsContract.AUTHORITY); 372 values.put(Directory.TYPE_RESOURCE_ID, R.string.default_directory); 373 values.put(DirectoryColumns.TYPE_RESOURCE_NAME, 374 mContext.getResources().getResourceName(R.string.default_directory)); 375 values.put(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_NONE); 376 values.put(Directory.SHORTCUT_SUPPORT, Directory.SHORTCUT_SUPPORT_FULL); 377 values.put(Directory.PHOTO_SUPPORT, Directory.PHOTO_SUPPORT_FULL); 378 db.replace(Tables.DIRECTORIES, null, values); 379 } 380 insertLocalInvisibleDirectory(SQLiteDatabase db)381 private void insertLocalInvisibleDirectory(SQLiteDatabase db) { 382 ContentValues values = new ContentValues(); 383 values.put(Directory._ID, Directory.LOCAL_INVISIBLE); 384 values.put(Directory.PACKAGE_NAME, mContext.getPackageName()); 385 values.put(Directory.DIRECTORY_AUTHORITY, ContactsContract.AUTHORITY); 386 values.put(Directory.TYPE_RESOURCE_ID, R.string.local_invisible_directory); 387 values.put(DirectoryColumns.TYPE_RESOURCE_NAME, 388 mContext.getResources().getResourceName(R.string.local_invisible_directory)); 389 values.put(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_NONE); 390 values.put(Directory.SHORTCUT_SUPPORT, Directory.SHORTCUT_SUPPORT_FULL); 391 values.put(Directory.PHOTO_SUPPORT, Directory.PHOTO_SUPPORT_FULL); 392 db.replace(Tables.DIRECTORIES, null, values); 393 } 394 395 /** 396 * Scans the specified package for content directories. The package may have 397 * already been removed, so packageName does not necessarily correspond to 398 * an installed package. 399 */ onPackageChanged(String packageName)400 public void onPackageChanged(String packageName) { 401 PackageInfo packageInfo = null; 402 403 try { 404 packageInfo = mPackageManager.getPackageInfo(packageName, 405 PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA); 406 } catch (NameNotFoundException e) { 407 // The package got removed 408 packageInfo = new PackageInfo(); 409 packageInfo.packageName = packageName; 410 } 411 412 if (mContext.getPackageName().equals(packageInfo.packageName)) { 413 if (DEBUG) Log.d(TAG, "Ignoring onPackageChanged for self"); 414 return; 415 } 416 updateDirectoriesForPackage(packageInfo, false); 417 } 418 419 420 /** 421 * Scans the specified package for content directories and updates the {@link Directory} 422 * table accordingly. 423 */ 424 @VisibleForTesting updateDirectoriesForPackage( PackageInfo packageInfo, boolean initialScan)425 List<DirectoryInfo> updateDirectoriesForPackage( 426 PackageInfo packageInfo, boolean initialScan) { 427 if (DEBUG) { 428 Log.d(TAG, "updateDirectoriesForPackage packageName=" + packageInfo.packageName 429 + " initialScan=" + initialScan); 430 } 431 432 ArrayList<DirectoryInfo> directories = Lists.newArrayList(); 433 434 if (Flags.stayStopped() 435 && (packageInfo.applicationInfo != null 436 && ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0))) { 437 if (DEBUG) { 438 Log.d(TAG, "Package " + packageInfo.packageName + " is in stopped state"); 439 } 440 return null; 441 } 442 443 ProviderInfo[] providers = packageInfo.providers; 444 if (providers != null) { 445 for (ProviderInfo provider : providers) { 446 if (isDirectoryProvider(provider)) { 447 queryDirectoriesForAuthority(directories, provider); 448 } 449 } 450 } 451 452 if (directories.size() == 0 && initialScan) { 453 return null; 454 } 455 456 SQLiteDatabase db = getDbHelper().getWritableDatabase(); 457 db.beginTransaction(); 458 try { 459 updateDirectories(db, directories); 460 // Clear out directories that are no longer present 461 StringBuilder sb = new StringBuilder(Directory.PACKAGE_NAME + "=?"); 462 if (!directories.isEmpty()) { 463 sb.append(" AND " + Directory._ID + " NOT IN("); 464 for (DirectoryInfo info: directories) { 465 sb.append(info.id).append(","); 466 } 467 sb.setLength(sb.length() - 1); // Remove the extra comma 468 sb.append(")"); 469 } 470 final int numDeleted = db.delete(Tables.DIRECTORIES, sb.toString(), 471 new String[] { packageInfo.packageName }); 472 if (DEBUG) { 473 Log.d(TAG, " deleted " + numDeleted + " stale rows"); 474 } 475 db.setTransactionSuccessful(); 476 } finally { 477 db.endTransaction(); 478 } 479 480 mContactsProvider.resetDirectoryCache(); 481 return directories; 482 } 483 484 /** 485 * Sends a {@link Directory#CONTENT_URI} request to a specific contact directory 486 * provider and appends all discovered directories to the directoryInfo list. 487 */ queryDirectoriesForAuthority( ArrayList<DirectoryInfo> directoryInfo, ProviderInfo provider)488 protected void queryDirectoriesForAuthority( 489 ArrayList<DirectoryInfo> directoryInfo, ProviderInfo provider) { 490 Uri uri = new Uri.Builder().scheme("content") 491 .authority(provider.authority).appendPath("directories").build(); 492 Cursor cursor = null; 493 try { 494 cursor = mContext.getContentResolver().query( 495 uri, DirectoryQuery.PROJECTION, null, null, null); 496 if (cursor == null) { 497 Log.i(TAG, providerDescription(provider) + " returned a NULL cursor."); 498 } else { 499 while (cursor.moveToNext()) { 500 DirectoryInfo info = new DirectoryInfo(); 501 info.packageName = provider.packageName; 502 info.authority = provider.authority; 503 info.accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME); 504 info.accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE); 505 info.displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME); 506 if (!cursor.isNull(DirectoryQuery.TYPE_RESOURCE_ID)) { 507 info.typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID); 508 } 509 if (!cursor.isNull(DirectoryQuery.EXPORT_SUPPORT)) { 510 int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT); 511 switch (exportSupport) { 512 case Directory.EXPORT_SUPPORT_NONE: 513 case Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY: 514 case Directory.EXPORT_SUPPORT_ANY_ACCOUNT: 515 info.exportSupport = exportSupport; 516 break; 517 default: 518 Log.e(TAG, providerDescription(provider) 519 + " - invalid export support flag: " + exportSupport); 520 } 521 } 522 if (!cursor.isNull(DirectoryQuery.SHORTCUT_SUPPORT)) { 523 int shortcutSupport = cursor.getInt(DirectoryQuery.SHORTCUT_SUPPORT); 524 switch (shortcutSupport) { 525 case Directory.SHORTCUT_SUPPORT_NONE: 526 case Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY: 527 case Directory.SHORTCUT_SUPPORT_FULL: 528 info.shortcutSupport = shortcutSupport; 529 break; 530 default: 531 Log.e(TAG, providerDescription(provider) 532 + " - invalid shortcut support flag: " + shortcutSupport); 533 } 534 } 535 if (!cursor.isNull(DirectoryQuery.PHOTO_SUPPORT)) { 536 int photoSupport = cursor.getInt(DirectoryQuery.PHOTO_SUPPORT); 537 switch (photoSupport) { 538 case Directory.PHOTO_SUPPORT_NONE: 539 case Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY: 540 case Directory.PHOTO_SUPPORT_FULL_SIZE_ONLY: 541 case Directory.PHOTO_SUPPORT_FULL: 542 info.photoSupport = photoSupport; 543 break; 544 default: 545 Log.e(TAG, providerDescription(provider) 546 + " - invalid photo support flag: " + photoSupport); 547 } 548 } 549 directoryInfo.add(info); 550 } 551 } 552 } catch (Throwable t) { 553 Log.e(TAG, providerDescription(provider) + " exception", t); 554 } finally { 555 if (cursor != null) { 556 cursor.close(); 557 } 558 } 559 } 560 561 /** 562 * Updates the directories tables in the database to match the info received 563 * from directory providers. 564 */ updateDirectories(SQLiteDatabase db, ArrayList<DirectoryInfo> directoryInfo)565 private void updateDirectories(SQLiteDatabase db, ArrayList<DirectoryInfo> directoryInfo) { 566 // Insert or replace existing directories. 567 // This happens so infrequently that we can use a less-then-optimal one-a-time approach 568 for (DirectoryInfo info : directoryInfo) { 569 ContentValues values = new ContentValues(); 570 values.put(Directory.PACKAGE_NAME, info.packageName); 571 values.put(Directory.DIRECTORY_AUTHORITY, info.authority); 572 values.put(Directory.ACCOUNT_NAME, info.accountName); 573 values.put(Directory.ACCOUNT_TYPE, info.accountType); 574 values.put(Directory.TYPE_RESOURCE_ID, info.typeResourceId); 575 values.put(Directory.DISPLAY_NAME, info.displayName); 576 values.put(Directory.EXPORT_SUPPORT, info.exportSupport); 577 values.put(Directory.SHORTCUT_SUPPORT, info.shortcutSupport); 578 values.put(Directory.PHOTO_SUPPORT, info.photoSupport); 579 580 if (info.typeResourceId != 0) { 581 String resourceName = getResourceNameById(info.packageName, info.typeResourceId); 582 values.put(DirectoryColumns.TYPE_RESOURCE_NAME, resourceName); 583 } 584 585 Cursor cursor = db.query(Tables.DIRECTORIES, new String[] { Directory._ID }, 586 Directory.PACKAGE_NAME + "=? AND " + Directory.DIRECTORY_AUTHORITY + "=? AND " 587 + Directory.ACCOUNT_NAME + "=? AND " + Directory.ACCOUNT_TYPE + "=?", 588 new String[] { 589 info.packageName, info.authority, info.accountName, info.accountType }, 590 null, null, null); 591 try { 592 long id; 593 if (cursor.moveToFirst()) { 594 id = cursor.getLong(0); 595 db.update(Tables.DIRECTORIES, values, Directory._ID + "=?", 596 new String[] { String.valueOf(id) }); 597 } else { 598 id = db.insert(Tables.DIRECTORIES, null, values); 599 } 600 info.id = id; 601 } finally { 602 cursor.close(); 603 } 604 } 605 } 606 providerDescription(ProviderInfo provider)607 protected String providerDescription(ProviderInfo provider) { 608 return "Directory provider " + provider.packageName + "(" + provider.authority + ")"; 609 } 610 } 611