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