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