ContentResolver.java revision 4c1241df8f8b7fd5ec3dff6c7e0f66271248e76e
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 an 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. 1084 * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}. 1085 * By default, CursorAdapter objects will get this notification. 1086 * 1087 * @param uri The uri of the content that was changed. 1088 * @param observer The observer that originated the change, may be <code>null</null>. 1089 * The observer that originated the change will only receive the notification if it 1090 * has requested to receive self-change notifications by implementing 1091 * {@link ContentObserver#deliverSelfNotifications()} to return true. 1092 */ 1093 public void notifyChange(Uri uri, ContentObserver observer) { 1094 notifyChange(uri, observer, true /* sync to network */); 1095 } 1096 1097 /** 1098 * Notify registered observers that a row was updated. 1099 * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}. 1100 * By default, CursorAdapter objects will get this notification. 1101 * 1102 * @param uri The uri of the content that was changed. 1103 * @param observer The observer that originated the change, may be <code>null</null>. 1104 * The observer that originated the change will only receive the notification if it 1105 * has requested to receive self-change notifications by implementing 1106 * {@link ContentObserver#deliverSelfNotifications()} to return true. 1107 * @param syncToNetwork If true, attempt to sync the change to the network. 1108 */ 1109 public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { 1110 try { 1111 getContentService().notifyChange( 1112 uri, observer == null ? null : observer.getContentObserver(), 1113 observer != null && observer.deliverSelfNotifications(), syncToNetwork); 1114 } catch (RemoteException e) { 1115 } 1116 } 1117 1118 /** 1119 * Start an asynchronous sync operation. If you want to monitor the progress 1120 * of the sync you may register a SyncObserver. Only values of the following 1121 * types may be used in the extras bundle: 1122 * <ul> 1123 * <li>Integer</li> 1124 * <li>Long</li> 1125 * <li>Boolean</li> 1126 * <li>Float</li> 1127 * <li>Double</li> 1128 * <li>String</li> 1129 * </ul> 1130 * 1131 * @param uri the uri of the provider to sync or null to sync all providers. 1132 * @param extras any extras to pass to the SyncAdapter. 1133 * @deprecated instead use 1134 * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)} 1135 */ 1136 @Deprecated 1137 public void startSync(Uri uri, Bundle extras) { 1138 Account account = null; 1139 if (extras != null) { 1140 String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT); 1141 if (!TextUtils.isEmpty(accountName)) { 1142 account = new Account(accountName, "com.google"); 1143 } 1144 extras.remove(SYNC_EXTRAS_ACCOUNT); 1145 } 1146 requestSync(account, uri != null ? uri.getAuthority() : null, extras); 1147 } 1148 1149 /** 1150 * Start an asynchronous sync operation. If you want to monitor the progress 1151 * of the sync you may register a SyncObserver. Only values of the following 1152 * types may be used in the extras bundle: 1153 * <ul> 1154 * <li>Integer</li> 1155 * <li>Long</li> 1156 * <li>Boolean</li> 1157 * <li>Float</li> 1158 * <li>Double</li> 1159 * <li>String</li> 1160 * </ul> 1161 * 1162 * @param account which account should be synced 1163 * @param authority which authority should be synced 1164 * @param extras any extras to pass to the SyncAdapter. 1165 */ 1166 public static void requestSync(Account account, String authority, Bundle extras) { 1167 validateSyncExtrasBundle(extras); 1168 try { 1169 getContentService().requestSync(account, authority, extras); 1170 } catch (RemoteException e) { 1171 } 1172 } 1173 1174 /** 1175 * Check that only values of the following types are in the Bundle: 1176 * <ul> 1177 * <li>Integer</li> 1178 * <li>Long</li> 1179 * <li>Boolean</li> 1180 * <li>Float</li> 1181 * <li>Double</li> 1182 * <li>String</li> 1183 * <li>Account</li> 1184 * <li>null</li> 1185 * </ul> 1186 * @param extras the Bundle to check 1187 */ 1188 public static void validateSyncExtrasBundle(Bundle extras) { 1189 try { 1190 for (String key : extras.keySet()) { 1191 Object value = extras.get(key); 1192 if (value == null) continue; 1193 if (value instanceof Long) continue; 1194 if (value instanceof Integer) continue; 1195 if (value instanceof Boolean) continue; 1196 if (value instanceof Float) continue; 1197 if (value instanceof Double) continue; 1198 if (value instanceof String) continue; 1199 if (value instanceof Account) continue; 1200 throw new IllegalArgumentException("unexpected value type: " 1201 + value.getClass().getName()); 1202 } 1203 } catch (IllegalArgumentException e) { 1204 throw e; 1205 } catch (RuntimeException exc) { 1206 throw new IllegalArgumentException("error unparceling Bundle", exc); 1207 } 1208 } 1209 1210 /** 1211 * Cancel any active or pending syncs that match the Uri. If the uri is null then 1212 * all syncs will be canceled. 1213 * 1214 * @param uri the uri of the provider to sync or null to sync all providers. 1215 * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)} 1216 */ 1217 @Deprecated 1218 public void cancelSync(Uri uri) { 1219 cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null); 1220 } 1221 1222 /** 1223 * Cancel any active or pending syncs that match account and authority. The account and 1224 * authority can each independently be set to null, which means that syncs with any account 1225 * or authority, respectively, will match. 1226 * 1227 * @param account filters the syncs that match by this account 1228 * @param authority filters the syncs that match by this authority 1229 */ 1230 public static void cancelSync(Account account, String authority) { 1231 try { 1232 getContentService().cancelSync(account, authority); 1233 } catch (RemoteException e) { 1234 } 1235 } 1236 1237 /** 1238 * Get information about the SyncAdapters that are known to the system. 1239 * @return an array of SyncAdapters that have registered with the system 1240 */ 1241 public static SyncAdapterType[] getSyncAdapterTypes() { 1242 try { 1243 return getContentService().getSyncAdapterTypes(); 1244 } catch (RemoteException e) { 1245 throw new RuntimeException("the ContentService should always be reachable", e); 1246 } 1247 } 1248 1249 /** 1250 * Check if the provider should be synced when a network tickle is received 1251 * 1252 * @param account the account whose setting we are querying 1253 * @param authority the provider whose setting we are querying 1254 * @return true if the provider should be synced when a network tickle is received 1255 */ 1256 public static boolean getSyncAutomatically(Account account, String authority) { 1257 try { 1258 return getContentService().getSyncAutomatically(account, authority); 1259 } catch (RemoteException e) { 1260 throw new RuntimeException("the ContentService should always be reachable", e); 1261 } 1262 } 1263 1264 /** 1265 * Set whether or not the provider is synced when it receives a network tickle. 1266 * 1267 * @param account the account whose setting we are querying 1268 * @param authority the provider whose behavior is being controlled 1269 * @param sync true if the provider should be synced when tickles are received for it 1270 */ 1271 public static void setSyncAutomatically(Account account, String authority, boolean sync) { 1272 try { 1273 getContentService().setSyncAutomatically(account, authority, sync); 1274 } catch (RemoteException e) { 1275 // exception ignored; if this is thrown then it means the runtime is in the midst of 1276 // being restarted 1277 } 1278 } 1279 1280 /** 1281 * Specifies that a sync should be requested with the specified the account, authority, 1282 * and extras at the given frequency. If there is already another periodic sync scheduled 1283 * with the account, authority and extras then a new periodic sync won't be added, instead 1284 * the frequency of the previous one will be updated. 1285 * <p> 1286 * These periodic syncs honor the "syncAutomatically" and "masterSyncAutomatically" settings. 1287 * Although these sync are scheduled at the specified frequency, it may take longer for it to 1288 * actually be started if other syncs are ahead of it in the sync operation queue. This means 1289 * that the actual start time may drift. 1290 * <p> 1291 * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY}, 1292 * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS}, 1293 * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE}, 1294 * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true. 1295 * If any are supplied then an {@link IllegalArgumentException} will be thrown. 1296 * 1297 * @param account the account to specify in the sync 1298 * @param authority the provider to specify in the sync request 1299 * @param extras extra parameters to go along with the sync request 1300 * @param pollFrequency how frequently the sync should be performed, in seconds. 1301 * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters 1302 * are null. 1303 */ 1304 public static void addPeriodicSync(Account account, String authority, Bundle extras, 1305 long pollFrequency) { 1306 validateSyncExtrasBundle(extras); 1307 if (account == null) { 1308 throw new IllegalArgumentException("account must not be null"); 1309 } 1310 if (authority == null) { 1311 throw new IllegalArgumentException("authority must not be null"); 1312 } 1313 if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false) 1314 || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false) 1315 || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false) 1316 || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false) 1317 || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false) 1318 || extras.getBoolean(SYNC_EXTRAS_FORCE, false) 1319 || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) { 1320 throw new IllegalArgumentException("illegal extras were set"); 1321 } 1322 try { 1323 getContentService().addPeriodicSync(account, authority, extras, pollFrequency); 1324 } catch (RemoteException e) { 1325 // exception ignored; if this is thrown then it means the runtime is in the midst of 1326 // being restarted 1327 } 1328 } 1329 1330 /** 1331 * Remove a periodic sync. Has no affect if account, authority and extras don't match 1332 * an existing periodic sync. 1333 * 1334 * @param account the account of the periodic sync to remove 1335 * @param authority the provider of the periodic sync to remove 1336 * @param extras the extras of the periodic sync to remove 1337 */ 1338 public static void removePeriodicSync(Account account, String authority, Bundle extras) { 1339 validateSyncExtrasBundle(extras); 1340 if (account == null) { 1341 throw new IllegalArgumentException("account must not be null"); 1342 } 1343 if (authority == null) { 1344 throw new IllegalArgumentException("authority must not be null"); 1345 } 1346 try { 1347 getContentService().removePeriodicSync(account, authority, extras); 1348 } catch (RemoteException e) { 1349 throw new RuntimeException("the ContentService should always be reachable", e); 1350 } 1351 } 1352 1353 /** 1354 * Get the list of information about the periodic syncs for the given account and authority. 1355 * 1356 * @param account the account whose periodic syncs we are querying 1357 * @param authority the provider whose periodic syncs we are querying 1358 * @return a list of PeriodicSync objects. This list may be empty but will never be null. 1359 */ 1360 public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) { 1361 if (account == null) { 1362 throw new IllegalArgumentException("account must not be null"); 1363 } 1364 if (authority == null) { 1365 throw new IllegalArgumentException("authority must not be null"); 1366 } 1367 try { 1368 return getContentService().getPeriodicSyncs(account, authority); 1369 } catch (RemoteException e) { 1370 throw new RuntimeException("the ContentService should always be reachable", e); 1371 } 1372 } 1373 1374 /** 1375 * Check if this account/provider is syncable. 1376 * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet. 1377 */ 1378 public static int getIsSyncable(Account account, String authority) { 1379 try { 1380 return getContentService().getIsSyncable(account, authority); 1381 } catch (RemoteException e) { 1382 throw new RuntimeException("the ContentService should always be reachable", e); 1383 } 1384 } 1385 1386 /** 1387 * Set whether this account/provider is syncable. 1388 * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown 1389 */ 1390 public static void setIsSyncable(Account account, String authority, int syncable) { 1391 try { 1392 getContentService().setIsSyncable(account, authority, syncable); 1393 } catch (RemoteException e) { 1394 // exception ignored; if this is thrown then it means the runtime is in the midst of 1395 // being restarted 1396 } 1397 } 1398 1399 /** 1400 * Gets the master auto-sync setting that applies to all the providers and accounts. 1401 * If this is false then the per-provider auto-sync setting is ignored. 1402 * 1403 * @return the master auto-sync setting that applies to all the providers and accounts 1404 */ 1405 public static boolean getMasterSyncAutomatically() { 1406 try { 1407 return getContentService().getMasterSyncAutomatically(); 1408 } catch (RemoteException e) { 1409 throw new RuntimeException("the ContentService should always be reachable", e); 1410 } 1411 } 1412 1413 /** 1414 * Sets the master auto-sync setting that applies to all the providers and accounts. 1415 * If this is false then the per-provider auto-sync setting is ignored. 1416 * 1417 * @param sync the master auto-sync setting that applies to all the providers and accounts 1418 */ 1419 public static void setMasterSyncAutomatically(boolean sync) { 1420 try { 1421 getContentService().setMasterSyncAutomatically(sync); 1422 } catch (RemoteException e) { 1423 // exception ignored; if this is thrown then it means the runtime is in the midst of 1424 // being restarted 1425 } 1426 } 1427 1428 /** 1429 * Returns true if there is currently a sync operation for the given 1430 * account or authority in the pending list, or actively being processed. 1431 * @param account the account whose setting we are querying 1432 * @param authority the provider whose behavior is being queried 1433 * @return true if a sync is active for the given account or authority. 1434 */ 1435 public static boolean isSyncActive(Account account, String authority) { 1436 try { 1437 return getContentService().isSyncActive(account, authority); 1438 } catch (RemoteException e) { 1439 throw new RuntimeException("the ContentService should always be reachable", e); 1440 } 1441 } 1442 1443 /** 1444 * If a sync is active returns the information about it, otherwise returns null. 1445 * <p> 1446 * @return the SyncInfo for the currently active sync or null if one is not active. 1447 * @deprecated 1448 * Since multiple concurrent syncs are now supported you should use 1449 * {@link #getCurrentSyncs()} to get the accurate list of current syncs. 1450 * This method returns the first item from the list of current syncs 1451 * or null if there are none. 1452 */ 1453 @Deprecated 1454 public static SyncInfo getCurrentSync() { 1455 try { 1456 final List<SyncInfo> syncs = getContentService().getCurrentSyncs(); 1457 if (syncs.isEmpty()) { 1458 return null; 1459 } 1460 return syncs.get(0); 1461 } catch (RemoteException e) { 1462 throw new RuntimeException("the ContentService should always be reachable", e); 1463 } 1464 } 1465 1466 /** 1467 * Returns a list with information about all the active syncs. This list will be empty 1468 * if there are no active syncs. 1469 * @return a List of SyncInfo objects for the currently active syncs. 1470 */ 1471 public static List<SyncInfo> getCurrentSyncs() { 1472 try { 1473 return getContentService().getCurrentSyncs(); 1474 } catch (RemoteException e) { 1475 throw new RuntimeException("the ContentService should always be reachable", e); 1476 } 1477 } 1478 1479 /** 1480 * Returns the status that matches the authority. 1481 * @param account the account whose setting we are querying 1482 * @param authority the provider whose behavior is being queried 1483 * @return the SyncStatusInfo for the authority, or null if none exists 1484 * @hide 1485 */ 1486 public static SyncStatusInfo getSyncStatus(Account account, String authority) { 1487 try { 1488 return getContentService().getSyncStatus(account, authority); 1489 } catch (RemoteException e) { 1490 throw new RuntimeException("the ContentService should always be reachable", e); 1491 } 1492 } 1493 1494 /** 1495 * Return true if the pending status is true of any matching authorities. 1496 * @param account the account whose setting we are querying 1497 * @param authority the provider whose behavior is being queried 1498 * @return true if there is a pending sync with the matching account and authority 1499 */ 1500 public static boolean isSyncPending(Account account, String authority) { 1501 try { 1502 return getContentService().isSyncPending(account, authority); 1503 } catch (RemoteException e) { 1504 throw new RuntimeException("the ContentService should always be reachable", e); 1505 } 1506 } 1507 1508 /** 1509 * Request notifications when the different aspects of the SyncManager change. The 1510 * different items that can be requested are: 1511 * <ul> 1512 * <li> {@link #SYNC_OBSERVER_TYPE_PENDING} 1513 * <li> {@link #SYNC_OBSERVER_TYPE_ACTIVE} 1514 * <li> {@link #SYNC_OBSERVER_TYPE_SETTINGS} 1515 * </ul> 1516 * The caller can set one or more of the status types in the mask for any 1517 * given listener registration. 1518 * @param mask the status change types that will cause the callback to be invoked 1519 * @param callback observer to be invoked when the status changes 1520 * @return a handle that can be used to remove the listener at a later time 1521 */ 1522 public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) { 1523 if (callback == null) { 1524 throw new IllegalArgumentException("you passed in a null callback"); 1525 } 1526 try { 1527 ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() { 1528 public void onStatusChanged(int which) throws RemoteException { 1529 callback.onStatusChanged(which); 1530 } 1531 }; 1532 getContentService().addStatusChangeListener(mask, observer); 1533 return observer; 1534 } catch (RemoteException e) { 1535 throw new RuntimeException("the ContentService should always be reachable", e); 1536 } 1537 } 1538 1539 /** 1540 * Remove a previously registered status change listener. 1541 * @param handle the handle that was returned by {@link #addStatusChangeListener} 1542 */ 1543 public static void removeStatusChangeListener(Object handle) { 1544 if (handle == null) { 1545 throw new IllegalArgumentException("you passed in a null handle"); 1546 } 1547 try { 1548 getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle); 1549 } catch (RemoteException e) { 1550 // exception ignored; if this is thrown then it means the runtime is in the midst of 1551 // being restarted 1552 } 1553 } 1554 1555 /** 1556 * Returns sampling percentage for a given duration. 1557 * 1558 * Always returns at least 1%. 1559 */ 1560 private int samplePercentForDuration(long durationMillis) { 1561 if (durationMillis >= SLOW_THRESHOLD_MILLIS) { 1562 return 100; 1563 } 1564 return (int) (100 * durationMillis / SLOW_THRESHOLD_MILLIS) + 1; 1565 } 1566 1567 private void maybeLogQueryToEventLog(long durationMillis, 1568 Uri uri, String[] projection, 1569 String selection, String sortOrder) { 1570 int samplePercent = samplePercentForDuration(durationMillis); 1571 if (samplePercent < 100) { 1572 synchronized (mRandom) { 1573 if (mRandom.nextInt(100) >= samplePercent) { 1574 return; 1575 } 1576 } 1577 } 1578 1579 StringBuilder projectionBuffer = new StringBuilder(100); 1580 if (projection != null) { 1581 for (int i = 0; i < projection.length; ++i) { 1582 // Note: not using a comma delimiter here, as the 1583 // multiple arguments to EventLog.writeEvent later 1584 // stringify with a comma delimiter, which would make 1585 // parsing uglier later. 1586 if (i != 0) projectionBuffer.append('/'); 1587 projectionBuffer.append(projection[i]); 1588 } 1589 } 1590 1591 // ActivityThread.currentPackageName() only returns non-null if the 1592 // current thread is an application main thread. This parameter tells 1593 // us whether an event loop is blocked, and if so, which app it is. 1594 String blockingPackage = AppGlobals.getInitialPackage(); 1595 1596 EventLog.writeEvent( 1597 EventLogTags.CONTENT_QUERY_SAMPLE, 1598 uri.toString(), 1599 projectionBuffer.toString(), 1600 selection != null ? selection : "", 1601 sortOrder != null ? sortOrder : "", 1602 durationMillis, 1603 blockingPackage != null ? blockingPackage : "", 1604 samplePercent); 1605 } 1606 1607 private void maybeLogUpdateToEventLog( 1608 long durationMillis, Uri uri, String operation, String selection) { 1609 int samplePercent = samplePercentForDuration(durationMillis); 1610 if (samplePercent < 100) { 1611 synchronized (mRandom) { 1612 if (mRandom.nextInt(100) >= samplePercent) { 1613 return; 1614 } 1615 } 1616 } 1617 String blockingPackage = AppGlobals.getInitialPackage(); 1618 EventLog.writeEvent( 1619 EventLogTags.CONTENT_UPDATE_SAMPLE, 1620 uri.toString(), 1621 operation, 1622 selection != null ? selection : "", 1623 durationMillis, 1624 blockingPackage != null ? blockingPackage : "", 1625 samplePercent); 1626 } 1627 1628 private final class CursorWrapperInner extends CrossProcessCursorWrapper { 1629 private final IContentProvider mContentProvider; 1630 public static final String TAG="CursorWrapperInner"; 1631 1632 private final CloseGuard mCloseGuard = CloseGuard.get(); 1633 private boolean mProviderReleased; 1634 1635 CursorWrapperInner(Cursor cursor, IContentProvider icp) { 1636 super(cursor); 1637 mContentProvider = icp; 1638 mCloseGuard.open("close"); 1639 } 1640 1641 @Override 1642 public void close() { 1643 super.close(); 1644 ContentResolver.this.releaseProvider(mContentProvider); 1645 mProviderReleased = true; 1646 1647 if (mCloseGuard != null) { 1648 mCloseGuard.close(); 1649 } 1650 } 1651 1652 @Override 1653 protected void finalize() throws Throwable { 1654 try { 1655 if (mCloseGuard != null) { 1656 mCloseGuard.warnIfOpen(); 1657 } 1658 1659 if (!mProviderReleased && mContentProvider != null) { 1660 // Even though we are using CloseGuard, log this anyway so that 1661 // application developers always see the message in the log. 1662 Log.w(TAG, "Cursor finalized without prior close()"); 1663 ContentResolver.this.releaseProvider(mContentProvider); 1664 } 1665 } finally { 1666 super.finalize(); 1667 } 1668 } 1669 } 1670 1671 private final class ParcelFileDescriptorInner extends ParcelFileDescriptor { 1672 private final IContentProvider mContentProvider; 1673 public static final String TAG="ParcelFileDescriptorInner"; 1674 private boolean mReleaseProviderFlag = false; 1675 1676 ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) { 1677 super(pfd); 1678 mContentProvider = icp; 1679 } 1680 1681 @Override 1682 public void close() throws IOException { 1683 if(!mReleaseProviderFlag) { 1684 super.close(); 1685 ContentResolver.this.releaseProvider(mContentProvider); 1686 mReleaseProviderFlag = true; 1687 } 1688 } 1689 1690 @Override 1691 protected void finalize() throws Throwable { 1692 if (!mReleaseProviderFlag) { 1693 close(); 1694 } 1695 } 1696 } 1697 1698 /** @hide */ 1699 public static final String CONTENT_SERVICE_NAME = "content"; 1700 1701 /** @hide */ 1702 public static IContentService getContentService() { 1703 if (sContentService != null) { 1704 return sContentService; 1705 } 1706 IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME); 1707 if (false) Log.v("ContentService", "default service binder = " + b); 1708 sContentService = IContentService.Stub.asInterface(b); 1709 if (false) Log.v("ContentService", "default service = " + sContentService); 1710 return sContentService; 1711 } 1712 1713 private static IContentService sContentService; 1714 private final Context mContext; 1715 private static final String TAG = "ContentResolver"; 1716} 1717