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