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