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