ContentResolver.java revision d72f718c9cc4bd5e4701f4c5cdab51b4d8cf6435
1/* 2 * Copyright (C) 2006 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 android.content; 18 19import android.accounts.Account; 20import android.app.ActivityThread; 21import android.content.pm.PackageManager.NameNotFoundException; 22import android.content.res.AssetFileDescriptor; 23import android.content.res.Resources; 24import android.database.ContentObserver; 25import android.database.Cursor; 26import android.database.CursorWrapper; 27import android.database.IContentObserver; 28import android.net.Uri; 29import android.os.Bundle; 30import android.os.IBinder; 31import android.os.ParcelFileDescriptor; 32import android.os.RemoteException; 33import android.os.ServiceManager; 34import android.os.SystemClock; 35import android.text.TextUtils; 36import android.util.Config; 37import android.util.EventLog; 38import android.util.Log; 39 40import java.io.File; 41import java.io.FileInputStream; 42import java.io.FileNotFoundException; 43import java.io.IOException; 44import java.io.InputStream; 45import java.io.OutputStream; 46import java.util.List; 47import java.util.Random; 48import java.util.ArrayList; 49 50 51/** 52 * This class provides applications access to the content model. 53 */ 54public abstract class ContentResolver { 55 /** 56 * @deprecated instead use 57 * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)} 58 */ 59 @Deprecated 60 public static final String SYNC_EXTRAS_ACCOUNT = "account"; 61 public static final String SYNC_EXTRAS_EXPEDITED = "expedited"; 62 /** 63 * @deprecated instead use 64 * {@link #SYNC_EXTRAS_MANUAL} 65 */ 66 @Deprecated 67 public static final String SYNC_EXTRAS_FORCE = "force"; 68 69 /** 70 * If this extra is set to true then the sync settings (like getSyncAutomatically()) 71 * are ignored by the sync scheduler. 72 */ 73 public static final String SYNC_EXTRAS_IGNORE_SETTINGS = "ignore_settings"; 74 75 /** 76 * If this extra is set to true then any backoffs for the initial attempt (e.g. due to retries) 77 * are ignored by the sync scheduler. If this request fails and gets rescheduled then the 78 * retries will still honor the backoff. 79 */ 80 public static final String SYNC_EXTRAS_IGNORE_BACKOFF = "ignore_backoff"; 81 82 /** 83 * If this extra is set to true then the request will not be retried if it fails. 84 */ 85 public static final String SYNC_EXTRAS_DO_NOT_RETRY = "do_not_retry"; 86 87 /** 88 * Setting this extra is the equivalent of setting both {@link #SYNC_EXTRAS_IGNORE_SETTINGS} 89 * and {@link #SYNC_EXTRAS_IGNORE_BACKOFF} 90 */ 91 public static final String SYNC_EXTRAS_MANUAL = "force"; 92 93 public static final String SYNC_EXTRAS_UPLOAD = "upload"; 94 public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override"; 95 public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions"; 96 97 /** 98 * Set by the SyncManager to request that the SyncAdapter initialize itself for 99 * the given account/authority pair. One required initialization step is to 100 * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been 101 * called with a >= 0 value. When this flag is set the SyncAdapter does not need to 102 * do a full sync, though it is allowed to do so. 103 */ 104 public static final String SYNC_EXTRAS_INITIALIZE = "initialize"; 105 106 public static final String SCHEME_CONTENT = "content"; 107 public static final String SCHEME_ANDROID_RESOURCE = "android.resource"; 108 public static final String SCHEME_FILE = "file"; 109 110 /** 111 * This is the Android platform's base MIME type for a content: URI 112 * containing a Cursor of a single item. Applications should use this 113 * as the base type along with their own sub-type of their content: URIs 114 * that represent a particular item. For example, hypothetical IMAP email 115 * client may have a URI 116 * <code>content://com.company.provider.imap/inbox/1</code> for a particular 117 * message in the inbox, whose MIME type would be reported as 118 * <code>CURSOR_ITEM_BASE_TYPE + "/vnd.company.imap-msg"</code> 119 * 120 * <p>Compare with {@link #CURSOR_DIR_BASE_TYPE}. 121 */ 122 public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item"; 123 124 /** 125 * This is the Android platform's base MIME type for a content: URI 126 * containing a Cursor of zero or more items. Applications should use this 127 * as the base type along with their own sub-type of their content: URIs 128 * that represent a directory of items. For example, hypothetical IMAP email 129 * client may have a URI 130 * <code>content://com.company.provider.imap/inbox</code> for all of the 131 * messages in its inbox, whose MIME type would be reported as 132 * <code>CURSOR_DIR_BASE_TYPE + "/vnd.company.imap-msg"</code> 133 * 134 * <p>Note how the base MIME type varies between this and 135 * {@link #CURSOR_ITEM_BASE_TYPE} depending on whether there is 136 * one single item or multiple items in the data set, while the sub-type 137 * remains the same because in either case the data structure contained 138 * in the cursor is the same. 139 */ 140 public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir"; 141 142 /** @hide */ 143 public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1; 144 /** @hide */ 145 public static final int SYNC_ERROR_AUTHENTICATION = 2; 146 /** @hide */ 147 public static final int SYNC_ERROR_IO = 3; 148 /** @hide */ 149 public static final int SYNC_ERROR_PARSE = 4; 150 /** @hide */ 151 public static final int SYNC_ERROR_CONFLICT = 5; 152 /** @hide */ 153 public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6; 154 /** @hide */ 155 public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7; 156 /** @hide */ 157 public static final int SYNC_ERROR_INTERNAL = 8; 158 159 /** @hide */ 160 public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0; 161 /** @hide */ 162 public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1; 163 /** @hide */ 164 public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2; 165 /** @hide */ 166 public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3; 167 /** @hide */ 168 public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff; 169 170 // Always log queries which take 100ms+; shorter queries are 171 // sampled accordingly. 172 private static final int SLOW_THRESHOLD_MILLIS = 100; 173 private final Random mRandom = new Random(); // guarded by itself 174 175 public ContentResolver(Context context) { 176 mContext = context; 177 } 178 179 /** @hide */ 180 protected abstract IContentProvider acquireProvider(Context c, String name); 181 /** @hide */ 182 public abstract boolean releaseProvider(IContentProvider icp); 183 184 /** 185 * Return the MIME type of the given content URL. 186 * 187 * @param url A Uri identifying content (either a list or specific type), 188 * using the content:// scheme. 189 * @return A MIME type for the content, or null if the URL is invalid or the type is unknown 190 */ 191 public final String getType(Uri url) 192 { 193 IContentProvider provider = acquireProvider(url); 194 if (provider == null) { 195 return null; 196 } 197 try { 198 return provider.getType(url); 199 } catch (RemoteException e) { 200 return null; 201 } catch (java.lang.Exception e) { 202 return null; 203 } finally { 204 releaseProvider(provider); 205 } 206 } 207 208 /** 209 * <p> 210 * Query the given URI, returning a {@link Cursor} over the result set. 211 * </p> 212 * <p> 213 * For best performance, the caller should follow these guidelines: 214 * <ul> 215 * <li>Provide an explicit projection, to prevent 216 * reading data from storage that aren't going to be used.</li> 217 * <li>Use question mark parameter markers such as 'phone=?' instead of 218 * explicit values in the {@code selection} parameter, so that queries 219 * that differ only by those values will be recognized as the same 220 * for caching purposes.</li> 221 * </ul> 222 * </p> 223 * 224 * @param uri The URI, using the content:// scheme, for the content to 225 * retrieve. 226 * @param projection A list of which columns to return. Passing null will 227 * return all columns, which is inefficient. 228 * @param selection A filter declaring which rows to return, formatted as an 229 * SQL WHERE clause (excluding the WHERE itself). Passing null will 230 * return all rows for the given URI. 231 * @param selectionArgs You may include ?s in selection, which will be 232 * replaced by the values from selectionArgs, in the order that they 233 * appear in the selection. The values will be bound as Strings. 234 * @param sortOrder How to order the rows, formatted as an SQL ORDER BY 235 * clause (excluding the ORDER BY itself). Passing null will use the 236 * default sort order, which may be unordered. 237 * @return A Cursor object, which is positioned before the first entry, or null 238 * @see Cursor 239 */ 240 public final Cursor query(Uri uri, String[] projection, 241 String selection, String[] selectionArgs, String sortOrder) { 242 IContentProvider provider = acquireProvider(uri); 243 if (provider == null) { 244 return null; 245 } 246 try { 247 long startTime = SystemClock.uptimeMillis(); 248 Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder); 249 if (qCursor == null) { 250 releaseProvider(provider); 251 return null; 252 } 253 long durationMillis = SystemClock.uptimeMillis() - startTime; 254 maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder); 255 // Wrap the cursor object into CursorWrapperInner object 256 return new CursorWrapperInner(qCursor, provider); 257 } catch (RemoteException e) { 258 releaseProvider(provider); 259 return null; 260 } catch(RuntimeException e) { 261 releaseProvider(provider); 262 throw e; 263 } 264 } 265 266 /** 267 * Open a stream on to the content associated with a content URI. If there 268 * is no data associated with the URI, FileNotFoundException is thrown. 269 * 270 * <h5>Accepts the following URI schemes:</h5> 271 * <ul> 272 * <li>content ({@link #SCHEME_CONTENT})</li> 273 * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li> 274 * <li>file ({@link #SCHEME_FILE})</li> 275 * </ul> 276 * 277 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information 278 * on these schemes. 279 * 280 * @param uri The desired URI. 281 * @return InputStream 282 * @throws FileNotFoundException if the provided URI could not be opened. 283 * @see #openAssetFileDescriptor(Uri, String) 284 */ 285 public final InputStream openInputStream(Uri uri) 286 throws FileNotFoundException { 287 String scheme = uri.getScheme(); 288 if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { 289 // Note: left here to avoid breaking compatibility. May be removed 290 // with sufficient testing. 291 OpenResourceIdResult r = getResourceId(uri); 292 try { 293 InputStream stream = r.r.openRawResource(r.id); 294 return stream; 295 } catch (Resources.NotFoundException ex) { 296 throw new FileNotFoundException("Resource does not exist: " + uri); 297 } 298 } else if (SCHEME_FILE.equals(scheme)) { 299 // Note: left here to avoid breaking compatibility. May be removed 300 // with sufficient testing. 301 return new FileInputStream(uri.getPath()); 302 } else { 303 AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r"); 304 try { 305 return fd != null ? fd.createInputStream() : null; 306 } catch (IOException e) { 307 throw new FileNotFoundException("Unable to create stream"); 308 } 309 } 310 } 311 312 /** 313 * Synonym for {@link #openOutputStream(Uri, String) 314 * openOutputStream(uri, "w")}. 315 * @throws FileNotFoundException if the provided URI could not be opened. 316 */ 317 public final OutputStream openOutputStream(Uri uri) 318 throws FileNotFoundException { 319 return openOutputStream(uri, "w"); 320 } 321 322 /** 323 * Open a stream on to the content associated with a content URI. If there 324 * is no data associated with the URI, FileNotFoundException is thrown. 325 * 326 * <h5>Accepts the following URI schemes:</h5> 327 * <ul> 328 * <li>content ({@link #SCHEME_CONTENT})</li> 329 * <li>file ({@link #SCHEME_FILE})</li> 330 * </ul> 331 * 332 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information 333 * on these schemes. 334 * 335 * @param uri The desired URI. 336 * @param mode May be "w", "wa", "rw", or "rwt". 337 * @return OutputStream 338 * @throws FileNotFoundException if the provided URI could not be opened. 339 * @see #openAssetFileDescriptor(Uri, String) 340 */ 341 public final OutputStream openOutputStream(Uri uri, String mode) 342 throws FileNotFoundException { 343 AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode); 344 try { 345 return fd != null ? fd.createOutputStream() : null; 346 } catch (IOException e) { 347 throw new FileNotFoundException("Unable to create stream"); 348 } 349 } 350 351 /** 352 * Open a raw file descriptor to access data under a "content:" URI. This 353 * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the 354 * underlying {@link ContentProvider#openFile} 355 * ContentProvider.openFile()} method, so will <em>not</em> work with 356 * providers that return sub-sections of files. If at all possible, 357 * you should use {@link #openAssetFileDescriptor(Uri, String)}. You 358 * will receive a FileNotFoundException exception if the provider returns a 359 * sub-section of a file. 360 * 361 * <h5>Accepts the following URI schemes:</h5> 362 * <ul> 363 * <li>content ({@link #SCHEME_CONTENT})</li> 364 * <li>file ({@link #SCHEME_FILE})</li> 365 * </ul> 366 * 367 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information 368 * on these schemes. 369 * 370 * @param uri The desired URI to open. 371 * @param mode The file mode to use, as per {@link ContentProvider#openFile 372 * ContentProvider.openFile}. 373 * @return Returns a new ParcelFileDescriptor pointing to the file. You 374 * own this descriptor and are responsible for closing it when done. 375 * @throws FileNotFoundException Throws FileNotFoundException of no 376 * file exists under the URI or the mode is invalid. 377 * @see #openAssetFileDescriptor(Uri, String) 378 */ 379 public final ParcelFileDescriptor openFileDescriptor(Uri uri, 380 String mode) throws FileNotFoundException { 381 AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode); 382 if (afd == null) { 383 return null; 384 } 385 386 if (afd.getDeclaredLength() < 0) { 387 // This is a full file! 388 return afd.getParcelFileDescriptor(); 389 } 390 391 // Client can't handle a sub-section of a file, so close what 392 // we got and bail with an exception. 393 try { 394 afd.close(); 395 } catch (IOException e) { 396 } 397 398 throw new FileNotFoundException("Not a whole file"); 399 } 400 401 /** 402 * Open a raw file descriptor to access data under a "content:" URI. This 403 * interacts with the underlying {@link ContentProvider#openAssetFile} 404 * ContentProvider.openAssetFile()} method of the provider associated with the 405 * given URI, to retrieve any file stored there. 406 * 407 * <h5>Accepts the following URI schemes:</h5> 408 * <ul> 409 * <li>content ({@link #SCHEME_CONTENT})</li> 410 * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li> 411 * <li>file ({@link #SCHEME_FILE})</li> 412 * </ul> 413 * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5> 414 * <p> 415 * A Uri object can be used to reference a resource in an APK file. The 416 * Uri should be one of the following formats: 417 * <ul> 418 * <li><code>android.resource://package_name/id_number</code><br/> 419 * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. 420 * For example <code>com.example.myapp</code><br/> 421 * <code>id_number</code> is the int form of the ID.<br/> 422 * The easiest way to construct this form is 423 * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre> 424 * </li> 425 * <li><code>android.resource://package_name/type/name</code><br/> 426 * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. 427 * For example <code>com.example.myapp</code><br/> 428 * <code>type</code> is the string form of the resource type. For example, <code>raw</code> 429 * or <code>drawable</code>. 430 * <code>name</code> is the string form of the resource name. That is, whatever the file 431 * name was in your res directory, without the type extension. 432 * The easiest way to construct this form is 433 * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre> 434 * </li> 435 * </ul> 436 * 437 * @param uri The desired URI to open. 438 * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile 439 * ContentProvider.openAssetFile}. 440 * @return Returns a new ParcelFileDescriptor pointing to the file. You 441 * own this descriptor and are responsible for closing it when done. 442 * @throws FileNotFoundException Throws FileNotFoundException of no 443 * file exists under the URI or the mode is invalid. 444 */ 445 public final AssetFileDescriptor openAssetFileDescriptor(Uri uri, 446 String mode) throws FileNotFoundException { 447 String scheme = uri.getScheme(); 448 if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { 449 if (!"r".equals(mode)) { 450 throw new FileNotFoundException("Can't write resources: " + uri); 451 } 452 OpenResourceIdResult r = getResourceId(uri); 453 try { 454 return r.r.openRawResourceFd(r.id); 455 } catch (Resources.NotFoundException ex) { 456 throw new FileNotFoundException("Resource does not exist: " + uri); 457 } 458 } else if (SCHEME_FILE.equals(scheme)) { 459 ParcelFileDescriptor pfd = ParcelFileDescriptor.open( 460 new File(uri.getPath()), modeToMode(uri, mode)); 461 return new AssetFileDescriptor(pfd, 0, -1); 462 } else { 463 IContentProvider provider = acquireProvider(uri); 464 if (provider == null) { 465 throw new FileNotFoundException("No content provider: " + uri); 466 } 467 try { 468 AssetFileDescriptor fd = provider.openAssetFile(uri, mode); 469 if(fd == null) { 470 releaseProvider(provider); 471 return null; 472 } 473 ParcelFileDescriptor pfd = new ParcelFileDescriptorInner( 474 fd.getParcelFileDescriptor(), provider); 475 return new AssetFileDescriptor(pfd, fd.getStartOffset(), 476 fd.getDeclaredLength()); 477 } catch (RemoteException e) { 478 releaseProvider(provider); 479 throw new FileNotFoundException("Dead content provider: " + uri); 480 } catch (FileNotFoundException e) { 481 releaseProvider(provider); 482 throw e; 483 } catch (RuntimeException e) { 484 releaseProvider(provider); 485 throw e; 486 } 487 } 488 } 489 490 /** 491 * A resource identified by the {@link Resources} that contains it, and a resource id. 492 * 493 * @hide 494 */ 495 public class OpenResourceIdResult { 496 public Resources r; 497 public int id; 498 } 499 500 /** 501 * Resolves an android.resource URI to a {@link Resources} and a resource id. 502 * 503 * @hide 504 */ 505 public OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException { 506 String authority = uri.getAuthority(); 507 Resources r; 508 if (TextUtils.isEmpty(authority)) { 509 throw new FileNotFoundException("No authority: " + uri); 510 } else { 511 try { 512 r = mContext.getPackageManager().getResourcesForApplication(authority); 513 } catch (NameNotFoundException ex) { 514 throw new FileNotFoundException("No package found for authority: " + uri); 515 } 516 } 517 List<String> path = uri.getPathSegments(); 518 if (path == null) { 519 throw new FileNotFoundException("No path: " + uri); 520 } 521 int len = path.size(); 522 int id; 523 if (len == 1) { 524 try { 525 id = Integer.parseInt(path.get(0)); 526 } catch (NumberFormatException e) { 527 throw new FileNotFoundException("Single path segment is not a resource ID: " + uri); 528 } 529 } else if (len == 2) { 530 id = r.getIdentifier(path.get(1), path.get(0), authority); 531 } else { 532 throw new FileNotFoundException("More than two path segments: " + uri); 533 } 534 if (id == 0) { 535 throw new FileNotFoundException("No resource found for: " + uri); 536 } 537 OpenResourceIdResult res = new OpenResourceIdResult(); 538 res.r = r; 539 res.id = id; 540 return res; 541 } 542 543 /** @hide */ 544 static public int modeToMode(Uri uri, String mode) throws FileNotFoundException { 545 int modeBits; 546 if ("r".equals(mode)) { 547 modeBits = ParcelFileDescriptor.MODE_READ_ONLY; 548 } else if ("w".equals(mode) || "wt".equals(mode)) { 549 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY 550 | ParcelFileDescriptor.MODE_CREATE 551 | ParcelFileDescriptor.MODE_TRUNCATE; 552 } else if ("wa".equals(mode)) { 553 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY 554 | ParcelFileDescriptor.MODE_CREATE 555 | ParcelFileDescriptor.MODE_APPEND; 556 } else if ("rw".equals(mode)) { 557 modeBits = ParcelFileDescriptor.MODE_READ_WRITE 558 | ParcelFileDescriptor.MODE_CREATE; 559 } else if ("rwt".equals(mode)) { 560 modeBits = ParcelFileDescriptor.MODE_READ_WRITE 561 | ParcelFileDescriptor.MODE_CREATE 562 | ParcelFileDescriptor.MODE_TRUNCATE; 563 } else { 564 throw new FileNotFoundException("Bad mode for " + uri + ": " 565 + mode); 566 } 567 return modeBits; 568 } 569 570 /** 571 * Inserts a row into a table at the given URL. 572 * 573 * If the content provider supports transactions the insertion will be atomic. 574 * 575 * @param url The URL of the table to insert into. 576 * @param values The initial values for the newly inserted row. The key is the column name for 577 * the field. Passing an empty ContentValues will create an empty row. 578 * @return the URL of the newly created row. 579 */ 580 public final Uri insert(Uri url, ContentValues values) 581 { 582 IContentProvider provider = acquireProvider(url); 583 if (provider == null) { 584 throw new IllegalArgumentException("Unknown URL " + url); 585 } 586 try { 587 long startTime = SystemClock.uptimeMillis(); 588 Uri createdRow = provider.insert(url, values); 589 long durationMillis = SystemClock.uptimeMillis() - startTime; 590 maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */); 591 return createdRow; 592 } catch (RemoteException e) { 593 return null; 594 } finally { 595 releaseProvider(provider); 596 } 597 } 598 599 /** 600 * Applies each of the {@link ContentProviderOperation} objects and returns an array 601 * of their results. Passes through OperationApplicationException, which may be thrown 602 * by the call to {@link ContentProviderOperation#apply}. 603 * If all the applications succeed then a {@link ContentProviderResult} array with the 604 * same number of elements as the operations will be returned. It is implementation-specific 605 * how many, if any, operations will have been successfully applied if a call to 606 * apply results in a {@link OperationApplicationException}. 607 * @param authority the authority of the ContentProvider to which this batch should be applied 608 * @param operations the operations to apply 609 * @return the results of the applications 610 * @throws OperationApplicationException thrown if an application fails. 611 * See {@link ContentProviderOperation#apply} for more information. 612 * @throws RemoteException thrown if a RemoteException is encountered while attempting 613 * to communicate with a remote provider. 614 */ 615 public ContentProviderResult[] applyBatch(String authority, 616 ArrayList<ContentProviderOperation> operations) 617 throws RemoteException, OperationApplicationException { 618 ContentProviderClient provider = acquireContentProviderClient(authority); 619 if (provider == null) { 620 throw new IllegalArgumentException("Unknown authority " + authority); 621 } 622 try { 623 return provider.applyBatch(operations); 624 } finally { 625 provider.release(); 626 } 627 } 628 629 /** 630 * Inserts multiple rows into a table at the given URL. 631 * 632 * This function make no guarantees about the atomicity of the insertions. 633 * 634 * @param url The URL of the table to insert into. 635 * @param values The initial values for the newly inserted rows. The key is the column name for 636 * the field. Passing null will create an empty row. 637 * @return the number of newly created rows. 638 */ 639 public final int bulkInsert(Uri url, ContentValues[] values) 640 { 641 IContentProvider provider = acquireProvider(url); 642 if (provider == null) { 643 throw new IllegalArgumentException("Unknown URL " + url); 644 } 645 try { 646 long startTime = SystemClock.uptimeMillis(); 647 int rowsCreated = provider.bulkInsert(url, values); 648 long durationMillis = SystemClock.uptimeMillis() - startTime; 649 maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */); 650 return rowsCreated; 651 } catch (RemoteException e) { 652 return 0; 653 } finally { 654 releaseProvider(provider); 655 } 656 } 657 658 /** 659 * Deletes row(s) specified by a content URI. 660 * 661 * If the content provider supports transactions, the deletion will be atomic. 662 * 663 * @param url The URL of the row to delete. 664 * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause 665 (excluding the WHERE itself). 666 * @return The number of rows deleted. 667 */ 668 public final int delete(Uri url, String where, String[] selectionArgs) 669 { 670 IContentProvider provider = acquireProvider(url); 671 if (provider == null) { 672 throw new IllegalArgumentException("Unknown URL " + url); 673 } 674 try { 675 long startTime = SystemClock.uptimeMillis(); 676 int rowsDeleted = provider.delete(url, where, selectionArgs); 677 long durationMillis = SystemClock.uptimeMillis() - startTime; 678 maybeLogUpdateToEventLog(durationMillis, url, "delete", where); 679 return rowsDeleted; 680 } catch (RemoteException e) { 681 return -1; 682 } finally { 683 releaseProvider(provider); 684 } 685 } 686 687 /** 688 * Update row(s) in a content URI. 689 * 690 * If the content provider supports transactions the update will be atomic. 691 * 692 * @param uri The URI to modify. 693 * @param values The new field values. The key is the column name for the field. 694 A null value will remove an existing field value. 695 * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause 696 (excluding the WHERE itself). 697 * @return The number of rows updated. 698 * @throws NullPointerException if uri or values are null 699 */ 700 public final int update(Uri uri, ContentValues values, String where, 701 String[] selectionArgs) { 702 IContentProvider provider = acquireProvider(uri); 703 if (provider == null) { 704 throw new IllegalArgumentException("Unknown URI " + uri); 705 } 706 try { 707 long startTime = SystemClock.uptimeMillis(); 708 int rowsUpdated = provider.update(uri, values, where, selectionArgs); 709 long durationMillis = SystemClock.uptimeMillis() - startTime; 710 maybeLogUpdateToEventLog(durationMillis, uri, "update", where); 711 return rowsUpdated; 712 } catch (RemoteException e) { 713 return -1; 714 } finally { 715 releaseProvider(provider); 716 } 717 } 718 719 /** 720 * Returns the content provider for the given content URI.. 721 * 722 * @param uri The URI to a content provider 723 * @return The ContentProvider for the given URI, or null if no content provider is found. 724 * @hide 725 */ 726 public final IContentProvider acquireProvider(Uri uri) 727 { 728 if (!SCHEME_CONTENT.equals(uri.getScheme())) { 729 return null; 730 } 731 String auth = uri.getAuthority(); 732 if (auth != null) { 733 return acquireProvider(mContext, uri.getAuthority()); 734 } 735 return null; 736 } 737 738 /** 739 * @hide 740 */ 741 public final IContentProvider acquireProvider(String name) { 742 if(name == null) { 743 return null; 744 } 745 return acquireProvider(mContext, name); 746 } 747 748 /** 749 * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 750 * that services the content at uri, starting the provider if necessary. Returns 751 * null if there is no provider associated wih the uri. The caller must indicate that they are 752 * done with the provider by calling {@link ContentProviderClient#release} which will allow 753 * the system to release the provider it it determines that there is no other reason for 754 * keeping it active. 755 * @param uri specifies which provider should be acquired 756 * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 757 * that services the content at uri or null if there isn't one. 758 */ 759 public final ContentProviderClient acquireContentProviderClient(Uri uri) { 760 IContentProvider provider = acquireProvider(uri); 761 if (provider != null) { 762 return new ContentProviderClient(this, provider); 763 } 764 765 return null; 766 } 767 768 /** 769 * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 770 * with the authority of name, starting the provider if necessary. Returns 771 * null if there is no provider associated wih the uri. The caller must indicate that they are 772 * done with the provider by calling {@link ContentProviderClient#release} which will allow 773 * the system to release the provider it it determines that there is no other reason for 774 * keeping it active. 775 * @param name specifies which provider should be acquired 776 * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 777 * with the authority of name or null if there isn't one. 778 */ 779 public final ContentProviderClient acquireContentProviderClient(String name) { 780 IContentProvider provider = acquireProvider(name); 781 if (provider != null) { 782 return new ContentProviderClient(this, provider); 783 } 784 785 return null; 786 } 787 788 /** 789 * Register an observer class that gets callbacks when data identified by a 790 * given content URI changes. 791 * 792 * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI 793 * for a whole class of content. 794 * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code> 795 * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI 796 * specified by <em>uri</em> will cause notifications to be sent. If true, than any URI values 797 * at or below the specified URI will also trigger a match. 798 * @param observer The object that receives callbacks when changes occur. 799 * @see #unregisterContentObserver 800 */ 801 public final void registerContentObserver(Uri uri, boolean notifyForDescendents, 802 ContentObserver observer) 803 { 804 try { 805 getContentService().registerContentObserver(uri, notifyForDescendents, 806 observer.getContentObserver()); 807 } catch (RemoteException e) { 808 } 809 } 810 811 /** 812 * Unregisters a change observer. 813 * 814 * @param observer The previously registered observer that is no longer needed. 815 * @see #registerContentObserver 816 */ 817 public final void unregisterContentObserver(ContentObserver observer) { 818 try { 819 IContentObserver contentObserver = observer.releaseContentObserver(); 820 if (contentObserver != null) { 821 getContentService().unregisterContentObserver( 822 contentObserver); 823 } 824 } catch (RemoteException e) { 825 } 826 } 827 828 /** 829 * Notify registered observers that a row was updated. 830 * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}. 831 * By default, CursorAdapter objects will get this notification. 832 * 833 * @param uri 834 * @param observer The observer that originated the change, may be <code>null</null> 835 */ 836 public void notifyChange(Uri uri, ContentObserver observer) { 837 notifyChange(uri, observer, true /* sync to network */); 838 } 839 840 /** 841 * Notify registered observers that a row was updated. 842 * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}. 843 * By default, CursorAdapter objects will get this notification. 844 * 845 * @param uri 846 * @param observer The observer that originated the change, may be <code>null</null> 847 * @param syncToNetwork If true, attempt to sync the change to the network. 848 */ 849 public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { 850 try { 851 getContentService().notifyChange( 852 uri, observer == null ? null : observer.getContentObserver(), 853 observer != null && observer.deliverSelfNotifications(), syncToNetwork); 854 } catch (RemoteException e) { 855 } 856 } 857 858 /** 859 * Start an asynchronous sync operation. If you want to monitor the progress 860 * of the sync you may register a SyncObserver. Only values of the following 861 * types may be used in the extras bundle: 862 * <ul> 863 * <li>Integer</li> 864 * <li>Long</li> 865 * <li>Boolean</li> 866 * <li>Float</li> 867 * <li>Double</li> 868 * <li>String</li> 869 * </ul> 870 * 871 * @param uri the uri of the provider to sync or null to sync all providers. 872 * @param extras any extras to pass to the SyncAdapter. 873 * @deprecated instead use 874 * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)} 875 */ 876 @Deprecated 877 public void startSync(Uri uri, Bundle extras) { 878 Account account = null; 879 if (extras != null) { 880 String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT); 881 if (!TextUtils.isEmpty(accountName)) { 882 account = new Account(accountName, "com.google"); 883 } 884 extras.remove(SYNC_EXTRAS_ACCOUNT); 885 } 886 requestSync(account, uri != null ? uri.getAuthority() : null, extras); 887 } 888 889 /** 890 * Start an asynchronous sync operation. If you want to monitor the progress 891 * of the sync you may register a SyncObserver. Only values of the following 892 * types may be used in the extras bundle: 893 * <ul> 894 * <li>Integer</li> 895 * <li>Long</li> 896 * <li>Boolean</li> 897 * <li>Float</li> 898 * <li>Double</li> 899 * <li>String</li> 900 * </ul> 901 * 902 * @param account which account should be synced 903 * @param authority which authority should be synced 904 * @param extras any extras to pass to the SyncAdapter. 905 */ 906 public static void requestSync(Account account, String authority, Bundle extras) { 907 validateSyncExtrasBundle(extras); 908 try { 909 getContentService().requestSync(account, authority, extras); 910 } catch (RemoteException e) { 911 } 912 } 913 914 /** 915 * Check that only values of the following types are in the Bundle: 916 * <ul> 917 * <li>Integer</li> 918 * <li>Long</li> 919 * <li>Boolean</li> 920 * <li>Float</li> 921 * <li>Double</li> 922 * <li>String</li> 923 * <li>Account</li> 924 * <li>null</li> 925 * </ul> 926 * @param extras the Bundle to check 927 */ 928 public static void validateSyncExtrasBundle(Bundle extras) { 929 try { 930 for (String key : extras.keySet()) { 931 Object value = extras.get(key); 932 if (value == null) continue; 933 if (value instanceof Long) continue; 934 if (value instanceof Integer) continue; 935 if (value instanceof Boolean) continue; 936 if (value instanceof Float) continue; 937 if (value instanceof Double) continue; 938 if (value instanceof String) continue; 939 if (value instanceof Account) continue; 940 throw new IllegalArgumentException("unexpected value type: " 941 + value.getClass().getName()); 942 } 943 } catch (IllegalArgumentException e) { 944 throw e; 945 } catch (RuntimeException exc) { 946 throw new IllegalArgumentException("error unparceling Bundle", exc); 947 } 948 } 949 950 /** 951 * Cancel any active or pending syncs that match the Uri. If the uri is null then 952 * all syncs will be canceled. 953 * 954 * @param uri the uri of the provider to sync or null to sync all providers. 955 * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)} 956 */ 957 @Deprecated 958 public void cancelSync(Uri uri) { 959 cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null); 960 } 961 962 /** 963 * Cancel any active or pending syncs that match account and authority. The account and 964 * authority can each independently be set to null, which means that syncs with any account 965 * or authority, respectively, will match. 966 * 967 * @param account filters the syncs that match by this account 968 * @param authority filters the syncs that match by this authority 969 */ 970 public static void cancelSync(Account account, String authority) { 971 try { 972 getContentService().cancelSync(account, authority); 973 } catch (RemoteException e) { 974 } 975 } 976 977 /** 978 * Get information about the SyncAdapters that are known to the system. 979 * @return an array of SyncAdapters that have registered with the system 980 */ 981 public static SyncAdapterType[] getSyncAdapterTypes() { 982 try { 983 return getContentService().getSyncAdapterTypes(); 984 } catch (RemoteException e) { 985 throw new RuntimeException("the ContentService should always be reachable", e); 986 } 987 } 988 989 /** 990 * Check if the provider should be synced when a network tickle is received 991 * 992 * @param account the account whose setting we are querying 993 * @param authority the provider whose setting we are querying 994 * @return true if the provider should be synced when a network tickle is received 995 */ 996 public static boolean getSyncAutomatically(Account account, String authority) { 997 try { 998 return getContentService().getSyncAutomatically(account, authority); 999 } catch (RemoteException e) { 1000 throw new RuntimeException("the ContentService should always be reachable", e); 1001 } 1002 } 1003 1004 /** 1005 * Set whether or not the provider is synced when it receives a network tickle. 1006 * 1007 * @param account the account whose setting we are querying 1008 * @param authority the provider whose behavior is being controlled 1009 * @param sync true if the provider should be synced when tickles are received for it 1010 */ 1011 public static void setSyncAutomatically(Account account, String authority, boolean sync) { 1012 try { 1013 getContentService().setSyncAutomatically(account, authority, sync); 1014 } catch (RemoteException e) { 1015 // exception ignored; if this is thrown then it means the runtime is in the midst of 1016 // being restarted 1017 } 1018 } 1019 1020 /** 1021 * Specifies that a sync should be requested with the specified the account, authority, 1022 * and extras at the given frequency. If there is already another periodic sync scheduled 1023 * with the account, authority and extras then a new periodic sync won't be added, instead 1024 * the frequency of the previous one will be updated. 1025 * <p> 1026 * These periodic syncs honor the "syncAutomatically" and "masterSyncAutomatically" settings. 1027 * Although these sync are scheduled at the specified frequency, it may take longer for it to 1028 * actually be started if other syncs are ahead of it in the sync operation queue. This means 1029 * that the actual start time may drift. 1030 * <p> 1031 * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY}, 1032 * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS}, 1033 * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE}, 1034 * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true. 1035 * If any are supplied then an {@link IllegalArgumentException} will be thrown. 1036 * 1037 * @param account the account to specify in the sync 1038 * @param authority the provider to specify in the sync request 1039 * @param extras extra parameters to go along with the sync request 1040 * @param pollFrequency how frequently the sync should be performed, in seconds. 1041 * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters 1042 * are null. 1043 */ 1044 public static void addPeriodicSync(Account account, String authority, Bundle extras, 1045 long pollFrequency) { 1046 validateSyncExtrasBundle(extras); 1047 if (account == null) { 1048 throw new IllegalArgumentException("account must not be null"); 1049 } 1050 if (authority == null) { 1051 throw new IllegalArgumentException("authority must not be null"); 1052 } 1053 if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false) 1054 || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false) 1055 || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false) 1056 || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false) 1057 || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false) 1058 || extras.getBoolean(SYNC_EXTRAS_FORCE, false) 1059 || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) { 1060 throw new IllegalArgumentException("illegal extras were set"); 1061 } 1062 try { 1063 getContentService().addPeriodicSync(account, authority, extras, pollFrequency); 1064 } catch (RemoteException e) { 1065 // exception ignored; if this is thrown then it means the runtime is in the midst of 1066 // being restarted 1067 } 1068 } 1069 1070 /** 1071 * Remove a periodic sync. Has no affect if account, authority and extras don't match 1072 * an existing periodic sync. 1073 * 1074 * @param account the account of the periodic sync to remove 1075 * @param authority the provider of the periodic sync to remove 1076 * @param extras the extras of the periodic sync to remove 1077 */ 1078 public static void removePeriodicSync(Account account, String authority, Bundle extras) { 1079 validateSyncExtrasBundle(extras); 1080 if (account == null) { 1081 throw new IllegalArgumentException("account must not be null"); 1082 } 1083 if (authority == null) { 1084 throw new IllegalArgumentException("authority must not be null"); 1085 } 1086 try { 1087 getContentService().removePeriodicSync(account, authority, extras); 1088 } catch (RemoteException e) { 1089 throw new RuntimeException("the ContentService should always be reachable", e); 1090 } 1091 } 1092 1093 /** 1094 * Get the list of information about the periodic syncs for the given account and authority. 1095 * 1096 * @param account the account whose periodic syncs we are querying 1097 * @param authority the provider whose periodic syncs we are querying 1098 * @return a list of PeriodicSync objects. This list may be empty but will never be null. 1099 */ 1100 public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) { 1101 if (account == null) { 1102 throw new IllegalArgumentException("account must not be null"); 1103 } 1104 if (authority == null) { 1105 throw new IllegalArgumentException("authority must not be null"); 1106 } 1107 try { 1108 return getContentService().getPeriodicSyncs(account, authority); 1109 } catch (RemoteException e) { 1110 throw new RuntimeException("the ContentService should always be reachable", e); 1111 } 1112 } 1113 1114 /** 1115 * Check if this account/provider is syncable. 1116 * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet. 1117 */ 1118 public static int getIsSyncable(Account account, String authority) { 1119 try { 1120 return getContentService().getIsSyncable(account, authority); 1121 } catch (RemoteException e) { 1122 throw new RuntimeException("the ContentService should always be reachable", e); 1123 } 1124 } 1125 1126 /** 1127 * Set whether this account/provider is syncable. 1128 * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown 1129 */ 1130 public static void setIsSyncable(Account account, String authority, int syncable) { 1131 try { 1132 getContentService().setIsSyncable(account, authority, syncable); 1133 } catch (RemoteException e) { 1134 // exception ignored; if this is thrown then it means the runtime is in the midst of 1135 // being restarted 1136 } 1137 } 1138 1139 /** 1140 * Gets the master auto-sync setting that applies to all the providers and accounts. 1141 * If this is false then the per-provider auto-sync setting is ignored. 1142 * 1143 * @return the master auto-sync setting that applies to all the providers and accounts 1144 */ 1145 public static boolean getMasterSyncAutomatically() { 1146 try { 1147 return getContentService().getMasterSyncAutomatically(); 1148 } catch (RemoteException e) { 1149 throw new RuntimeException("the ContentService should always be reachable", e); 1150 } 1151 } 1152 1153 /** 1154 * Sets the master auto-sync setting that applies to all the providers and accounts. 1155 * If this is false then the per-provider auto-sync setting is ignored. 1156 * 1157 * @param sync the master auto-sync setting that applies to all the providers and accounts 1158 */ 1159 public static void setMasterSyncAutomatically(boolean sync) { 1160 try { 1161 getContentService().setMasterSyncAutomatically(sync); 1162 } catch (RemoteException e) { 1163 // exception ignored; if this is thrown then it means the runtime is in the midst of 1164 // being restarted 1165 } 1166 } 1167 1168 /** 1169 * Returns true if there is currently a sync operation for the given 1170 * account or authority in the pending list, or actively being processed. 1171 * @param account the account whose setting we are querying 1172 * @param authority the provider whose behavior is being queried 1173 * @return true if a sync is active for the given account or authority. 1174 */ 1175 public static boolean isSyncActive(Account account, String authority) { 1176 try { 1177 return getContentService().isSyncActive(account, authority); 1178 } catch (RemoteException e) { 1179 throw new RuntimeException("the ContentService should always be reachable", e); 1180 } 1181 } 1182 1183 /** 1184 * If a sync is active returns the information about it, otherwise returns false. 1185 * @return the ActiveSyncInfo for the currently active sync or null if one is not active. 1186 * @hide 1187 */ 1188 public static ActiveSyncInfo getActiveSync() { 1189 try { 1190 return getContentService().getActiveSync(); 1191 } catch (RemoteException e) { 1192 throw new RuntimeException("the ContentService should always be reachable", e); 1193 } 1194 } 1195 1196 /** 1197 * Returns the status that matches the authority. 1198 * @param account the account whose setting we are querying 1199 * @param authority the provider whose behavior is being queried 1200 * @return the SyncStatusInfo for the authority, or null if none exists 1201 * @hide 1202 */ 1203 public static SyncStatusInfo getSyncStatus(Account account, String authority) { 1204 try { 1205 return getContentService().getSyncStatus(account, authority); 1206 } catch (RemoteException e) { 1207 throw new RuntimeException("the ContentService should always be reachable", e); 1208 } 1209 } 1210 1211 /** 1212 * Return true if the pending status is true of any matching authorities. 1213 * @param account the account whose setting we are querying 1214 * @param authority the provider whose behavior is being queried 1215 * @return true if there is a pending sync with the matching account and authority 1216 */ 1217 public static boolean isSyncPending(Account account, String authority) { 1218 try { 1219 return getContentService().isSyncPending(account, authority); 1220 } catch (RemoteException e) { 1221 throw new RuntimeException("the ContentService should always be reachable", e); 1222 } 1223 } 1224 1225 public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) { 1226 try { 1227 ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() { 1228 public void onStatusChanged(int which) throws RemoteException { 1229 callback.onStatusChanged(which); 1230 } 1231 }; 1232 getContentService().addStatusChangeListener(mask, observer); 1233 return observer; 1234 } catch (RemoteException e) { 1235 throw new RuntimeException("the ContentService should always be reachable", e); 1236 } 1237 } 1238 1239 public static void removeStatusChangeListener(Object handle) { 1240 try { 1241 getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle); 1242 } catch (RemoteException e) { 1243 // exception ignored; if this is thrown then it means the runtime is in the midst of 1244 // being restarted 1245 } 1246 } 1247 1248 /** 1249 * Returns sampling percentage for a given duration. 1250 * 1251 * Always returns at least 1%. 1252 */ 1253 private int samplePercentForDuration(long durationMillis) { 1254 if (durationMillis >= SLOW_THRESHOLD_MILLIS) { 1255 return 100; 1256 } 1257 return (int) (100 * durationMillis / SLOW_THRESHOLD_MILLIS) + 1; 1258 } 1259 1260 private void maybeLogQueryToEventLog(long durationMillis, 1261 Uri uri, String[] projection, 1262 String selection, String sortOrder) { 1263 int samplePercent = samplePercentForDuration(durationMillis); 1264 if (samplePercent < 100) { 1265 synchronized (mRandom) { 1266 if (mRandom.nextInt(100) >= samplePercent) { 1267 return; 1268 } 1269 } 1270 } 1271 1272 StringBuilder projectionBuffer = new StringBuilder(100); 1273 if (projection != null) { 1274 for (int i = 0; i < projection.length; ++i) { 1275 // Note: not using a comma delimiter here, as the 1276 // multiple arguments to EventLog.writeEvent later 1277 // stringify with a comma delimiter, which would make 1278 // parsing uglier later. 1279 if (i != 0) projectionBuffer.append('/'); 1280 projectionBuffer.append(projection[i]); 1281 } 1282 } 1283 1284 // ActivityThread.currentPackageName() only returns non-null if the 1285 // current thread is an application main thread. This parameter tells 1286 // us whether an event loop is blocked, and if so, which app it is. 1287 String blockingPackage = ActivityThread.currentPackageName(); 1288 1289 EventLog.writeEvent( 1290 EventLogTags.CONTENT_QUERY_OPERATION, 1291 uri.toString(), 1292 projectionBuffer.toString(), 1293 selection != null ? selection : "", 1294 sortOrder != null ? sortOrder : "", 1295 durationMillis, 1296 blockingPackage != null ? blockingPackage : "", 1297 samplePercent); 1298 } 1299 1300 private void maybeLogUpdateToEventLog( 1301 long durationMillis, Uri uri, String operation, String selection) { 1302 int samplePercent = samplePercentForDuration(durationMillis); 1303 if (samplePercent < 100) { 1304 synchronized (mRandom) { 1305 if (mRandom.nextInt(100) >= samplePercent) { 1306 return; 1307 } 1308 } 1309 } 1310 String blockingPackage = ActivityThread.currentPackageName(); 1311 EventLog.writeEvent( 1312 EventLogTags.CONTENT_UPDATE_OPERATION, 1313 uri.toString(), 1314 operation, 1315 selection != null ? selection : "", 1316 durationMillis, 1317 blockingPackage != null ? blockingPackage : "", 1318 samplePercent); 1319 } 1320 1321 private final class CursorWrapperInner extends CursorWrapper { 1322 private IContentProvider mContentProvider; 1323 public static final String TAG="CursorWrapperInner"; 1324 private boolean mCloseFlag = false; 1325 1326 CursorWrapperInner(Cursor cursor, IContentProvider icp) { 1327 super(cursor); 1328 mContentProvider = icp; 1329 } 1330 1331 @Override 1332 public void close() { 1333 super.close(); 1334 ContentResolver.this.releaseProvider(mContentProvider); 1335 mCloseFlag = true; 1336 } 1337 1338 @Override 1339 protected void finalize() throws Throwable { 1340 try { 1341 if(!mCloseFlag) { 1342 ContentResolver.this.releaseProvider(mContentProvider); 1343 } 1344 } finally { 1345 super.finalize(); 1346 } 1347 } 1348 } 1349 1350 private final class ParcelFileDescriptorInner extends ParcelFileDescriptor { 1351 private IContentProvider mContentProvider; 1352 public static final String TAG="ParcelFileDescriptorInner"; 1353 private boolean mReleaseProviderFlag = false; 1354 1355 ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) { 1356 super(pfd); 1357 mContentProvider = icp; 1358 } 1359 1360 @Override 1361 public void close() throws IOException { 1362 if(!mReleaseProviderFlag) { 1363 super.close(); 1364 ContentResolver.this.releaseProvider(mContentProvider); 1365 mReleaseProviderFlag = true; 1366 } 1367 } 1368 1369 @Override 1370 protected void finalize() throws Throwable { 1371 if (!mReleaseProviderFlag) { 1372 close(); 1373 } 1374 } 1375 } 1376 1377 /** @hide */ 1378 public static final String CONTENT_SERVICE_NAME = "content"; 1379 1380 /** @hide */ 1381 public static IContentService getContentService() { 1382 if (sContentService != null) { 1383 return sContentService; 1384 } 1385 IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME); 1386 if (Config.LOGV) Log.v("ContentService", "default service binder = " + b); 1387 sContentService = IContentService.Stub.asInterface(b); 1388 if (Config.LOGV) Log.v("ContentService", "default service = " + sContentService); 1389 return sContentService; 1390 } 1391 1392 private static IContentService sContentService; 1393 private final Context mContext; 1394 private static final String TAG = "ContentResolver"; 1395} 1396