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