ContentResolver.java revision 5bba632d877c2878384ff21566c8eb6a1a22f37b
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.content.pm.PackageManager.NameNotFoundException; 20import android.content.res.AssetFileDescriptor; 21import android.content.res.Resources; 22import android.database.ContentObserver; 23import android.database.Cursor; 24import android.database.CursorWrapper; 25import android.database.IContentObserver; 26import android.net.Uri; 27import android.os.Bundle; 28import android.os.IBinder; 29import android.os.ParcelFileDescriptor; 30import android.os.RemoteException; 31import android.os.ServiceManager; 32import android.text.TextUtils; 33import android.accounts.Account; 34import android.util.Config; 35import android.util.Log; 36 37import java.io.File; 38import java.io.FileInputStream; 39import java.io.FileNotFoundException; 40import java.io.IOException; 41import java.io.InputStream; 42import java.io.OutputStream; 43import java.util.List; 44import java.util.ArrayList; 45 46 47/** 48 * This class provides applications access to the content model. 49 */ 50public abstract class ContentResolver { 51 /** 52 * @deprecated instead use 53 * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)} 54 */ 55 @Deprecated 56 public static final String SYNC_EXTRAS_ACCOUNT = "account"; 57 public static final String SYNC_EXTRAS_EXPEDITED = "expedited"; 58 /** 59 * @deprecated instead use 60 * {@link #SYNC_EXTRAS_MANUAL} 61 */ 62 @Deprecated 63 public static final String SYNC_EXTRAS_FORCE = "force"; 64 public static final String SYNC_EXTRAS_MANUAL = "force"; 65 public static final String SYNC_EXTRAS_UPLOAD = "upload"; 66 public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override"; 67 public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions"; 68 69 /** 70 * Set by the SyncManager to request that the SyncAdapter initialize itself for 71 * the given account/authority pair. One required initialization step is to 72 * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been 73 * called with a >= 0 value. When this flag is set the SyncAdapter does not need to 74 * do a full sync, though it is allowed to do so. 75 */ 76 public static final String SYNC_EXTRAS_INITIALIZE = "initialize"; 77 78 public static final String SCHEME_CONTENT = "content"; 79 public static final String SCHEME_ANDROID_RESOURCE = "android.resource"; 80 public static final String SCHEME_FILE = "file"; 81 82 /** 83 * This is the Android platform's base MIME type for a content: URI 84 * containing a Cursor of a single item. Applications should use this 85 * as the base type along with their own sub-type of their content: URIs 86 * that represent a particular item. For example, hypothetical IMAP email 87 * client may have a URI 88 * <code>content://com.company.provider.imap/inbox/1</code> for a particular 89 * message in the inbox, whose MIME type would be reported as 90 * <code>CURSOR_ITEM_BASE_TYPE + "/vnd.company.imap-msg"</code> 91 * 92 * <p>Compare with {@link #CURSOR_DIR_BASE_TYPE}. 93 */ 94 public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item"; 95 96 /** 97 * This is the Android platform's base MIME type for a content: URI 98 * containing a Cursor of zero or more items. Applications should use this 99 * as the base type along with their own sub-type of their content: URIs 100 * that represent a directory of items. For example, hypothetical IMAP email 101 * client may have a URI 102 * <code>content://com.company.provider.imap/inbox</code> for all of the 103 * messages in its inbox, whose MIME type would be reported as 104 * <code>CURSOR_DIR_BASE_TYPE + "/vnd.company.imap-msg"</code> 105 * 106 * <p>Note how the base MIME type varies between this and 107 * {@link #CURSOR_ITEM_BASE_TYPE} depending on whether there is 108 * one single item or multiple items in the data set, while the sub-type 109 * remains the same because in either case the data structure contained 110 * in the cursor is the same. 111 */ 112 public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir"; 113 114 /** @hide */ 115 public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1; 116 /** @hide */ 117 public static final int SYNC_ERROR_AUTHENTICATION = 2; 118 /** @hide */ 119 public static final int SYNC_ERROR_IO = 3; 120 /** @hide */ 121 public static final int SYNC_ERROR_PARSE = 4; 122 /** @hide */ 123 public static final int SYNC_ERROR_CONFLICT = 5; 124 /** @hide */ 125 public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6; 126 /** @hide */ 127 public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7; 128 /** @hide */ 129 public static final int SYNC_ERROR_INTERNAL = 8; 130 131 /** @hide */ 132 public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0; 133 /** @hide */ 134 public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1; 135 /** @hide */ 136 public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2; 137 /** @hide */ 138 public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3; 139 /** @hide */ 140 public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff; 141 142 public ContentResolver(Context context) { 143 mContext = context; 144 } 145 146 /** @hide */ 147 protected abstract IContentProvider acquireProvider(Context c, String name); 148 /** @hide */ 149 public abstract boolean releaseProvider(IContentProvider icp); 150 151 /** 152 * Return the MIME type of the given content URL. 153 * 154 * @param url A Uri identifying content (either a list or specific type), 155 * using the content:// scheme. 156 * @return A MIME type for the content, or null if the URL is invalid or the type is unknown 157 */ 158 public final String getType(Uri url) 159 { 160 IContentProvider provider = acquireProvider(url); 161 if (provider == null) { 162 return null; 163 } 164 try { 165 return provider.getType(url); 166 } catch (RemoteException e) { 167 return null; 168 } catch (java.lang.Exception e) { 169 return null; 170 } finally { 171 releaseProvider(provider); 172 } 173 } 174 175 /** 176 * Query the given URI, returning a {@link Cursor} over the result set. 177 * 178 * @param uri The URI, using the content:// scheme, for the content to 179 * retrieve. 180 * @param projection A list of which columns to return. Passing null will 181 * return all columns, which is discouraged to prevent reading data 182 * from storage that isn't going to be used. 183 * @param selection A filter declaring which rows to return, formatted as an 184 * SQL WHERE clause (excluding the WHERE itself). Passing null will 185 * return all rows for the given URI. 186 * @param selectionArgs You may include ?s in selection, which will be 187 * replaced by the values from selectionArgs, in the order that they 188 * appear in the selection. The values will be bound as Strings. 189 * @param sortOrder How to order the rows, formatted as an SQL ORDER BY 190 * clause (excluding the ORDER BY itself). Passing null will use the 191 * default sort order, which may be unordered. 192 * @return A Cursor object, which is positioned before the first entry, or null 193 * @see Cursor 194 */ 195 public final Cursor query(Uri uri, String[] projection, 196 String selection, String[] selectionArgs, String sortOrder) { 197 IContentProvider provider = acquireProvider(uri); 198 if (provider == null) { 199 return null; 200 } 201 try { 202 Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder); 203 if(qCursor == null) { 204 releaseProvider(provider); 205 return null; 206 } 207 //Wrap the cursor object into CursorWrapperInner object 208 return new CursorWrapperInner(qCursor, provider); 209 } catch (RemoteException e) { 210 releaseProvider(provider); 211 return null; 212 } catch(RuntimeException e) { 213 releaseProvider(provider); 214 throw e; 215 } 216 } 217 218 /** 219 * EntityIterator wrapper that releases the associated ContentProviderClient when the 220 * iterator is closed. 221 * @hide 222 */ 223 private class EntityIteratorWrapper implements EntityIterator { 224 private final EntityIterator mInner; 225 private final ContentProviderClient mClient; 226 private volatile boolean mClientReleased; 227 228 EntityIteratorWrapper(EntityIterator inner, ContentProviderClient client) { 229 mInner = inner; 230 mClient = client; 231 mClientReleased = false; 232 } 233 234 public boolean hasNext() throws RemoteException { 235 if (mClientReleased) { 236 throw new IllegalStateException("this iterator is already closed"); 237 } 238 return mInner.hasNext(); 239 } 240 241 public Entity next() throws RemoteException { 242 if (mClientReleased) { 243 throw new IllegalStateException("this iterator is already closed"); 244 } 245 return mInner.next(); 246 } 247 248 public void reset() throws RemoteException { 249 if (mClientReleased) { 250 throw new IllegalStateException("this iterator is already closed"); 251 } 252 mInner.reset(); 253 } 254 255 public void close() { 256 mClient.release(); 257 mInner.close(); 258 mClientReleased = true; 259 } 260 261 protected void finalize() throws Throwable { 262 if (!mClientReleased) { 263 mClient.release(); 264 } 265 super.finalize(); 266 } 267 } 268 269 /** 270 * Query the given URI, returning an {@link EntityIterator} over the result set. 271 * 272 * @param uri The URI, using the content:// scheme, for the content to 273 * retrieve. 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 An EntityIterator object 284 * @throws RemoteException thrown if a RemoteException is encountered while attempting 285 * to communicate with a remote provider. 286 * @throws IllegalArgumentException thrown if there is no provider that matches the uri 287 * @hide 288 */ 289 public final EntityIterator queryEntities(Uri uri, 290 String selection, String[] selectionArgs, String sortOrder) throws RemoteException { 291 ContentProviderClient provider = acquireContentProviderClient(uri); 292 if (provider == null) { 293 throw new IllegalArgumentException("Unknown URL " + uri); 294 } 295 try { 296 EntityIterator entityIterator = 297 provider.queryEntities(uri, selection, selectionArgs, sortOrder); 298 return new EntityIteratorWrapper(entityIterator, provider); 299 } catch(RuntimeException e) { 300 provider.release(); 301 throw e; 302 } catch(RemoteException e) { 303 provider.release(); 304 throw e; 305 } 306 } 307 308 /** 309 * Open a stream on to the content associated with a content URI. If there 310 * is no data associated with the URI, FileNotFoundException is thrown. 311 * 312 * <h5>Accepts the following URI schemes:</h5> 313 * <ul> 314 * <li>content ({@link #SCHEME_CONTENT})</li> 315 * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li> 316 * <li>file ({@link #SCHEME_FILE})</li> 317 * </ul> 318 * 319 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information 320 * on these schemes. 321 * 322 * @param uri The desired URI. 323 * @return InputStream 324 * @throws FileNotFoundException if the provided URI could not be opened. 325 * @see #openAssetFileDescriptor(Uri, String) 326 */ 327 public final InputStream openInputStream(Uri uri) 328 throws FileNotFoundException { 329 String scheme = uri.getScheme(); 330 if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { 331 // Note: left here to avoid breaking compatibility. May be removed 332 // with sufficient testing. 333 OpenResourceIdResult r = getResourceId(uri); 334 try { 335 InputStream stream = r.r.openRawResource(r.id); 336 return stream; 337 } catch (Resources.NotFoundException ex) { 338 throw new FileNotFoundException("Resource does not exist: " + uri); 339 } 340 } else if (SCHEME_FILE.equals(scheme)) { 341 // Note: left here to avoid breaking compatibility. May be removed 342 // with sufficient testing. 343 return new FileInputStream(uri.getPath()); 344 } else { 345 AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r"); 346 try { 347 return fd != null ? fd.createInputStream() : null; 348 } catch (IOException e) { 349 throw new FileNotFoundException("Unable to create stream"); 350 } 351 } 352 } 353 354 /** 355 * Synonym for {@link #openOutputStream(Uri, String) 356 * openOutputStream(uri, "w")}. 357 * @throws FileNotFoundException if the provided URI could not be opened. 358 */ 359 public final OutputStream openOutputStream(Uri uri) 360 throws FileNotFoundException { 361 return openOutputStream(uri, "w"); 362 } 363 364 /** 365 * Open a stream on to the content associated with a content URI. If there 366 * is no data associated with the URI, FileNotFoundException is thrown. 367 * 368 * <h5>Accepts the following URI schemes:</h5> 369 * <ul> 370 * <li>content ({@link #SCHEME_CONTENT})</li> 371 * <li>file ({@link #SCHEME_FILE})</li> 372 * </ul> 373 * 374 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information 375 * on these schemes. 376 * 377 * @param uri The desired URI. 378 * @param mode May be "w", "wa", "rw", or "rwt". 379 * @return OutputStream 380 * @throws FileNotFoundException if the provided URI could not be opened. 381 * @see #openAssetFileDescriptor(Uri, String) 382 */ 383 public final OutputStream openOutputStream(Uri uri, String mode) 384 throws FileNotFoundException { 385 AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode); 386 try { 387 return fd != null ? fd.createOutputStream() : null; 388 } catch (IOException e) { 389 throw new FileNotFoundException("Unable to create stream"); 390 } 391 } 392 393 /** 394 * Open a raw file descriptor to access data under a "content:" URI. This 395 * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the 396 * underlying {@link ContentProvider#openFile} 397 * ContentProvider.openFile()} method, so will <em>not</em> work with 398 * providers that return sub-sections of files. If at all possible, 399 * you should use {@link #openAssetFileDescriptor(Uri, String)}. You 400 * will receive a FileNotFoundException exception if the provider returns a 401 * sub-section of a file. 402 * 403 * <h5>Accepts the following URI schemes:</h5> 404 * <ul> 405 * <li>content ({@link #SCHEME_CONTENT})</li> 406 * <li>file ({@link #SCHEME_FILE})</li> 407 * </ul> 408 * 409 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information 410 * on these schemes. 411 * 412 * @param uri The desired URI to open. 413 * @param mode The file mode to use, as per {@link ContentProvider#openFile 414 * ContentProvider.openFile}. 415 * @return Returns a new ParcelFileDescriptor pointing to the file. You 416 * own this descriptor and are responsible for closing it when done. 417 * @throws FileNotFoundException Throws FileNotFoundException of no 418 * file exists under the URI or the mode is invalid. 419 * @see #openAssetFileDescriptor(Uri, String) 420 */ 421 public final ParcelFileDescriptor openFileDescriptor(Uri uri, 422 String mode) throws FileNotFoundException { 423 AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode); 424 if (afd == null) { 425 return null; 426 } 427 428 if (afd.getDeclaredLength() < 0) { 429 // This is a full file! 430 return afd.getParcelFileDescriptor(); 431 } 432 433 // Client can't handle a sub-section of a file, so close what 434 // we got and bail with an exception. 435 try { 436 afd.close(); 437 } catch (IOException e) { 438 } 439 440 throw new FileNotFoundException("Not a whole file"); 441 } 442 443 /** 444 * Open a raw file descriptor to access data under a "content:" URI. This 445 * interacts with the underlying {@link ContentProvider#openAssetFile} 446 * ContentProvider.openAssetFile()} method of the provider associated with the 447 * given URI, to retrieve any file stored there. 448 * 449 * <h5>Accepts the following URI schemes:</h5> 450 * <ul> 451 * <li>content ({@link #SCHEME_CONTENT})</li> 452 * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li> 453 * <li>file ({@link #SCHEME_FILE})</li> 454 * </ul> 455 * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5> 456 * <p> 457 * A Uri object can be used to reference a resource in an APK file. The 458 * Uri should be one of the following formats: 459 * <ul> 460 * <li><code>android.resource://package_name/id_number</code><br/> 461 * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. 462 * For example <code>com.example.myapp</code><br/> 463 * <code>id_number</code> is the int form of the ID.<br/> 464 * The easiest way to construct this form is 465 * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre> 466 * </li> 467 * <li><code>android.resource://package_name/type/name</code><br/> 468 * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. 469 * For example <code>com.example.myapp</code><br/> 470 * <code>type</code> is the string form of the resource type. For example, <code>raw</code> 471 * or <code>drawable</code>. 472 * <code>name</code> is the string form of the resource name. That is, whatever the file 473 * name was in your res directory, without the type extension. 474 * The easiest way to construct this form is 475 * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre> 476 * </li> 477 * </ul> 478 * 479 * @param uri The desired URI to open. 480 * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile 481 * ContentProvider.openAssetFile}. 482 * @return Returns a new ParcelFileDescriptor pointing to the file. You 483 * own this descriptor and are responsible for closing it when done. 484 * @throws FileNotFoundException Throws FileNotFoundException of no 485 * file exists under the URI or the mode is invalid. 486 */ 487 public final AssetFileDescriptor openAssetFileDescriptor(Uri uri, 488 String mode) throws FileNotFoundException { 489 String scheme = uri.getScheme(); 490 if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { 491 if (!"r".equals(mode)) { 492 throw new FileNotFoundException("Can't write resources: " + uri); 493 } 494 OpenResourceIdResult r = getResourceId(uri); 495 try { 496 return r.r.openRawResourceFd(r.id); 497 } catch (Resources.NotFoundException ex) { 498 throw new FileNotFoundException("Resource does not exist: " + uri); 499 } 500 } else if (SCHEME_FILE.equals(scheme)) { 501 ParcelFileDescriptor pfd = ParcelFileDescriptor.open( 502 new File(uri.getPath()), modeToMode(uri, mode)); 503 return new AssetFileDescriptor(pfd, 0, -1); 504 } else { 505 IContentProvider provider = acquireProvider(uri); 506 if (provider == null) { 507 throw new FileNotFoundException("No content provider: " + uri); 508 } 509 try { 510 AssetFileDescriptor fd = provider.openAssetFile(uri, mode); 511 if(fd == null) { 512 releaseProvider(provider); 513 return null; 514 } 515 ParcelFileDescriptor pfd = new ParcelFileDescriptorInner( 516 fd.getParcelFileDescriptor(), provider); 517 return new AssetFileDescriptor(pfd, fd.getStartOffset(), 518 fd.getDeclaredLength()); 519 } catch (RemoteException e) { 520 releaseProvider(provider); 521 throw new FileNotFoundException("Dead content provider: " + uri); 522 } catch (FileNotFoundException e) { 523 releaseProvider(provider); 524 throw e; 525 } catch (RuntimeException e) { 526 releaseProvider(provider); 527 throw e; 528 } 529 } 530 } 531 532 /** 533 * A resource identified by the {@link Resources} that contains it, and a resource id. 534 * 535 * @hide 536 */ 537 public class OpenResourceIdResult { 538 public Resources r; 539 public int id; 540 } 541 542 /** 543 * Resolves an android.resource URI to a {@link Resources} and a resource id. 544 * 545 * @hide 546 */ 547 public OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException { 548 String authority = uri.getAuthority(); 549 Resources r; 550 if (TextUtils.isEmpty(authority)) { 551 throw new FileNotFoundException("No authority: " + uri); 552 } else { 553 try { 554 r = mContext.getPackageManager().getResourcesForApplication(authority); 555 } catch (NameNotFoundException ex) { 556 throw new FileNotFoundException("No package found for authority: " + uri); 557 } 558 } 559 List<String> path = uri.getPathSegments(); 560 if (path == null) { 561 throw new FileNotFoundException("No path: " + uri); 562 } 563 int len = path.size(); 564 int id; 565 if (len == 1) { 566 try { 567 id = Integer.parseInt(path.get(0)); 568 } catch (NumberFormatException e) { 569 throw new FileNotFoundException("Single path segment is not a resource ID: " + uri); 570 } 571 } else if (len == 2) { 572 id = r.getIdentifier(path.get(1), path.get(0), authority); 573 } else { 574 throw new FileNotFoundException("More than two path segments: " + uri); 575 } 576 if (id == 0) { 577 throw new FileNotFoundException("No resource found for: " + uri); 578 } 579 OpenResourceIdResult res = new OpenResourceIdResult(); 580 res.r = r; 581 res.id = id; 582 return res; 583 } 584 585 /** @hide */ 586 static public int modeToMode(Uri uri, String mode) throws FileNotFoundException { 587 int modeBits; 588 if ("r".equals(mode)) { 589 modeBits = ParcelFileDescriptor.MODE_READ_ONLY; 590 } else if ("w".equals(mode) || "wt".equals(mode)) { 591 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY 592 | ParcelFileDescriptor.MODE_CREATE 593 | ParcelFileDescriptor.MODE_TRUNCATE; 594 } else if ("wa".equals(mode)) { 595 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY 596 | ParcelFileDescriptor.MODE_CREATE 597 | ParcelFileDescriptor.MODE_APPEND; 598 } else if ("rw".equals(mode)) { 599 modeBits = ParcelFileDescriptor.MODE_READ_WRITE 600 | ParcelFileDescriptor.MODE_CREATE; 601 } else if ("rwt".equals(mode)) { 602 modeBits = ParcelFileDescriptor.MODE_READ_WRITE 603 | ParcelFileDescriptor.MODE_CREATE 604 | ParcelFileDescriptor.MODE_TRUNCATE; 605 } else { 606 throw new FileNotFoundException("Bad mode for " + uri + ": " 607 + mode); 608 } 609 return modeBits; 610 } 611 612 /** 613 * Inserts a row into a table at the given URL. 614 * 615 * If the content provider supports transactions the insertion will be atomic. 616 * 617 * @param url The URL of the table to insert into. 618 * @param values The initial values for the newly inserted row. The key is the column name for 619 * the field. Passing an empty ContentValues will create an empty row. 620 * @return the URL of the newly created row. 621 */ 622 public final Uri insert(Uri url, ContentValues values) 623 { 624 IContentProvider provider = acquireProvider(url); 625 if (provider == null) { 626 throw new IllegalArgumentException("Unknown URL " + url); 627 } 628 try { 629 return provider.insert(url, values); 630 } catch (RemoteException e) { 631 return null; 632 } finally { 633 releaseProvider(provider); 634 } 635 } 636 637 /** 638 * Applies each of the {@link ContentProviderOperation} objects and returns an array 639 * of their results. Passes through OperationApplicationException, which may be thrown 640 * by the call to {@link ContentProviderOperation#apply}. 641 * If all the applications succeed then a {@link ContentProviderResult} array with the 642 * same number of elements as the operations will be returned. It is implementation-specific 643 * how many, if any, operations will have been successfully applied if a call to 644 * apply results in a {@link OperationApplicationException}. 645 * @param authority the authority of the ContentProvider to which this batch should be applied 646 * @param operations the operations to apply 647 * @return the results of the applications 648 * @throws OperationApplicationException thrown if an application fails. 649 * See {@link ContentProviderOperation#apply} for more information. 650 * @throws RemoteException thrown if a RemoteException is encountered while attempting 651 * to communicate with a remote provider. 652 */ 653 public ContentProviderResult[] applyBatch(String authority, 654 ArrayList<ContentProviderOperation> operations) 655 throws RemoteException, OperationApplicationException { 656 ContentProviderClient provider = acquireContentProviderClient(authority); 657 if (provider == null) { 658 throw new IllegalArgumentException("Unknown authority " + authority); 659 } 660 try { 661 return provider.applyBatch(operations); 662 } finally { 663 provider.release(); 664 } 665 } 666 667 /** 668 * Inserts multiple rows into a table at the given URL. 669 * 670 * This function make no guarantees about the atomicity of the insertions. 671 * 672 * @param url The URL of the table to insert into. 673 * @param values The initial values for the newly inserted rows. The key is the column name for 674 * the field. Passing null will create an empty row. 675 * @return the number of newly created rows. 676 */ 677 public final int bulkInsert(Uri url, ContentValues[] values) 678 { 679 IContentProvider provider = acquireProvider(url); 680 if (provider == null) { 681 throw new IllegalArgumentException("Unknown URL " + url); 682 } 683 try { 684 return provider.bulkInsert(url, values); 685 } catch (RemoteException e) { 686 return 0; 687 } finally { 688 releaseProvider(provider); 689 } 690 } 691 692 /** 693 * Deletes row(s) specified by a content URI. 694 * 695 * If the content provider supports transactions, the deletion will be atomic. 696 * 697 * @param url The URL of the row to delete. 698 * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause 699 (excluding the WHERE itself). 700 * @return The number of rows deleted. 701 */ 702 public final int delete(Uri url, String where, String[] selectionArgs) 703 { 704 IContentProvider provider = acquireProvider(url); 705 if (provider == null) { 706 throw new IllegalArgumentException("Unknown URL " + url); 707 } 708 try { 709 return provider.delete(url, where, selectionArgs); 710 } catch (RemoteException e) { 711 return -1; 712 } finally { 713 releaseProvider(provider); 714 } 715 } 716 717 /** 718 * Update row(s) in a content URI. 719 * 720 * If the content provider supports transactions the update will be atomic. 721 * 722 * @param uri The URI to modify. 723 * @param values The new field values. The key is the column name for the field. 724 A null value will remove an existing field value. 725 * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause 726 (excluding the WHERE itself). 727 * @return The number of rows updated. 728 * @throws NullPointerException if uri or values are null 729 */ 730 public final int update(Uri uri, ContentValues values, String where, 731 String[] selectionArgs) { 732 IContentProvider provider = acquireProvider(uri); 733 if (provider == null) { 734 throw new IllegalArgumentException("Unknown URI " + uri); 735 } 736 try { 737 return provider.update(uri, values, where, selectionArgs); 738 } catch (RemoteException e) { 739 return -1; 740 } finally { 741 releaseProvider(provider); 742 } 743 } 744 745 /** 746 * Returns the content provider for the given content URI.. 747 * 748 * @param uri The URI to a content provider 749 * @return The ContentProvider for the given URI, or null if no content provider is found. 750 * @hide 751 */ 752 public final IContentProvider acquireProvider(Uri uri) 753 { 754 if (!SCHEME_CONTENT.equals(uri.getScheme())) { 755 return null; 756 } 757 String auth = uri.getAuthority(); 758 if (auth != null) { 759 return acquireProvider(mContext, uri.getAuthority()); 760 } 761 return null; 762 } 763 764 /** 765 * @hide 766 */ 767 public final IContentProvider acquireProvider(String name) { 768 if(name == null) { 769 return null; 770 } 771 return acquireProvider(mContext, name); 772 } 773 774 /** 775 * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 776 * that services the content at uri, starting the provider if necessary. Returns 777 * null if there is no provider associated wih the uri. The caller must indicate that they are 778 * done with the provider by calling {@link ContentProviderClient#release} which will allow 779 * the system to release the provider it it determines that there is no other reason for 780 * keeping it active. 781 * @param uri specifies which provider should be acquired 782 * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 783 * that services the content at uri or null if there isn't one. 784 */ 785 public final ContentProviderClient acquireContentProviderClient(Uri uri) { 786 IContentProvider provider = acquireProvider(uri); 787 if (provider != null) { 788 return new ContentProviderClient(this, provider); 789 } 790 791 return null; 792 } 793 794 /** 795 * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 796 * with the authority of name, starting the provider if necessary. Returns 797 * null if there is no provider associated wih the uri. The caller must indicate that they are 798 * done with the provider by calling {@link ContentProviderClient#release} which will allow 799 * the system to release the provider it it determines that there is no other reason for 800 * keeping it active. 801 * @param name specifies which provider should be acquired 802 * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 803 * with the authority of name or null if there isn't one. 804 */ 805 public final ContentProviderClient acquireContentProviderClient(String name) { 806 IContentProvider provider = acquireProvider(name); 807 if (provider != null) { 808 return new ContentProviderClient(this, provider); 809 } 810 811 return null; 812 } 813 814 /** 815 * Register an observer class that gets callbacks when data identified by a 816 * given content URI changes. 817 * 818 * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI 819 * for a whole class of content. 820 * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code> 821 * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI 822 * specified by <em>uri</em> will cause notifications to be sent. If true, than any URI values 823 * at or below the specified URI will also trigger a match. 824 * @param observer The object that receives callbacks when changes occur. 825 * @see #unregisterContentObserver 826 */ 827 public final void registerContentObserver(Uri uri, boolean notifyForDescendents, 828 ContentObserver observer) 829 { 830 try { 831 getContentService().registerContentObserver(uri, notifyForDescendents, 832 observer.getContentObserver()); 833 } catch (RemoteException e) { 834 } 835 } 836 837 /** 838 * Unregisters a change observer. 839 * 840 * @param observer The previously registered observer that is no longer needed. 841 * @see #registerContentObserver 842 */ 843 public final void unregisterContentObserver(ContentObserver observer) { 844 try { 845 IContentObserver contentObserver = observer.releaseContentObserver(); 846 if (contentObserver != null) { 847 getContentService().unregisterContentObserver( 848 contentObserver); 849 } 850 } catch (RemoteException e) { 851 } 852 } 853 854 /** 855 * Notify registered observers that a row was updated. 856 * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}. 857 * By default, CursorAdapter objects will get this notification. 858 * 859 * @param uri 860 * @param observer The observer that originated the change, may be <code>null</null> 861 */ 862 public void notifyChange(Uri uri, ContentObserver observer) { 863 notifyChange(uri, observer, true /* sync to network */); 864 } 865 866 /** 867 * Notify registered observers that a row was updated. 868 * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}. 869 * By default, CursorAdapter objects will get this notification. 870 * 871 * @param uri 872 * @param observer The observer that originated the change, may be <code>null</null> 873 * @param syncToNetwork If true, attempt to sync the change to the network. 874 */ 875 public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { 876 try { 877 getContentService().notifyChange( 878 uri, observer == null ? null : observer.getContentObserver(), 879 observer != null && observer.deliverSelfNotifications(), syncToNetwork); 880 } catch (RemoteException e) { 881 } 882 } 883 884 /** 885 * Start an asynchronous sync operation. If you want to monitor the progress 886 * of the sync you may register a SyncObserver. Only values of the following 887 * types may be used in the extras bundle: 888 * <ul> 889 * <li>Integer</li> 890 * <li>Long</li> 891 * <li>Boolean</li> 892 * <li>Float</li> 893 * <li>Double</li> 894 * <li>String</li> 895 * </ul> 896 * 897 * @param uri the uri of the provider to sync or null to sync all providers. 898 * @param extras any extras to pass to the SyncAdapter. 899 * @deprecated instead use 900 * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)} 901 */ 902 @Deprecated 903 public void startSync(Uri uri, Bundle extras) { 904 Account account = null; 905 if (extras != null) { 906 String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT); 907 if (!TextUtils.isEmpty(accountName)) { 908 account = new Account(accountName, "com.google"); 909 } 910 extras.remove(SYNC_EXTRAS_ACCOUNT); 911 } 912 requestSync(account, uri != null ? uri.getAuthority() : null, extras); 913 } 914 915 /** 916 * Start an asynchronous sync operation. If you want to monitor the progress 917 * of the sync you may register a SyncObserver. Only values of the following 918 * types may be used in the extras bundle: 919 * <ul> 920 * <li>Integer</li> 921 * <li>Long</li> 922 * <li>Boolean</li> 923 * <li>Float</li> 924 * <li>Double</li> 925 * <li>String</li> 926 * </ul> 927 * 928 * @param account which account should be synced 929 * @param authority which authority should be synced 930 * @param extras any extras to pass to the SyncAdapter. 931 */ 932 public static void requestSync(Account account, String authority, Bundle extras) { 933 validateSyncExtrasBundle(extras); 934 try { 935 getContentService().requestSync(account, authority, extras); 936 } catch (RemoteException e) { 937 } 938 } 939 940 /** 941 * Check that only values of the following types are in the Bundle: 942 * <ul> 943 * <li>Integer</li> 944 * <li>Long</li> 945 * <li>Boolean</li> 946 * <li>Float</li> 947 * <li>Double</li> 948 * <li>String</li> 949 * <li>Account</li> 950 * <li>null</li> 951 * </ul> 952 * @param extras the Bundle to check 953 */ 954 public static void validateSyncExtrasBundle(Bundle extras) { 955 try { 956 for (String key : extras.keySet()) { 957 Object value = extras.get(key); 958 if (value == null) continue; 959 if (value instanceof Long) continue; 960 if (value instanceof Integer) continue; 961 if (value instanceof Boolean) continue; 962 if (value instanceof Float) continue; 963 if (value instanceof Double) continue; 964 if (value instanceof String) continue; 965 if (value instanceof Account) continue; 966 throw new IllegalArgumentException("unexpected value type: " 967 + value.getClass().getName()); 968 } 969 } catch (IllegalArgumentException e) { 970 throw e; 971 } catch (RuntimeException exc) { 972 throw new IllegalArgumentException("error unparceling Bundle", exc); 973 } 974 } 975 976 /** 977 * Cancel any active or pending syncs that match the Uri. If the uri is null then 978 * all syncs will be canceled. 979 * 980 * @param uri the uri of the provider to sync or null to sync all providers. 981 * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)} 982 */ 983 @Deprecated 984 public void cancelSync(Uri uri) { 985 cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null); 986 } 987 988 /** 989 * Cancel any active or pending syncs that match account and authority. The account and 990 * authority can each independently be set to null, which means that syncs with any account 991 * or authority, respectively, will match. 992 * 993 * @param account filters the syncs that match by this account 994 * @param authority filters the syncs that match by this authority 995 */ 996 public static void cancelSync(Account account, String authority) { 997 try { 998 getContentService().cancelSync(account, authority); 999 } catch (RemoteException e) { 1000 } 1001 } 1002 1003 /** 1004 * Get information about the SyncAdapters that are known to the system. 1005 * @return an array of SyncAdapters that have registered with the system 1006 */ 1007 public static SyncAdapterType[] getSyncAdapterTypes() { 1008 try { 1009 return getContentService().getSyncAdapterTypes(); 1010 } catch (RemoteException e) { 1011 throw new RuntimeException("the ContentService should always be reachable", e); 1012 } 1013 } 1014 1015 /** 1016 * Check if the provider should be synced when a network tickle is received 1017 * 1018 * @param account the account whose setting we are querying 1019 * @param authority the provider whose setting we are querying 1020 * @return true if the provider should be synced when a network tickle is received 1021 */ 1022 public static boolean getSyncAutomatically(Account account, String authority) { 1023 try { 1024 return getContentService().getSyncAutomatically(account, authority); 1025 } catch (RemoteException e) { 1026 throw new RuntimeException("the ContentService should always be reachable", e); 1027 } 1028 } 1029 1030 /** 1031 * Set whether or not the provider is synced when it receives a network tickle. 1032 * 1033 * @param account the account whose setting we are querying 1034 * @param authority the provider whose behavior is being controlled 1035 * @param sync true if the provider should be synced when tickles are received for it 1036 */ 1037 public static void setSyncAutomatically(Account account, String authority, boolean sync) { 1038 try { 1039 getContentService().setSyncAutomatically(account, authority, sync); 1040 } catch (RemoteException e) { 1041 // exception ignored; if this is thrown then it means the runtime is in the midst of 1042 // being restarted 1043 } 1044 } 1045 1046 /** 1047 * Check if this account/provider is syncable. 1048 * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet. 1049 */ 1050 public static int getIsSyncable(Account account, String authority) { 1051 try { 1052 return getContentService().getIsSyncable(account, authority); 1053 } catch (RemoteException e) { 1054 throw new RuntimeException("the ContentService should always be reachable", e); 1055 } 1056 } 1057 1058 /** 1059 * Set whether this account/provider is syncable. 1060 * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown 1061 */ 1062 public static void setIsSyncable(Account account, String authority, int syncable) { 1063 try { 1064 getContentService().setIsSyncable(account, authority, syncable); 1065 } catch (RemoteException e) { 1066 // exception ignored; if this is thrown then it means the runtime is in the midst of 1067 // being restarted 1068 } 1069 } 1070 1071 /** 1072 * Gets the master auto-sync setting that applies to all the providers and accounts. 1073 * If this is false then the per-provider auto-sync setting is ignored. 1074 * 1075 * @return the master auto-sync setting that applies to all the providers and accounts 1076 */ 1077 public static boolean getMasterSyncAutomatically() { 1078 try { 1079 return getContentService().getMasterSyncAutomatically(); 1080 } catch (RemoteException e) { 1081 throw new RuntimeException("the ContentService should always be reachable", e); 1082 } 1083 } 1084 1085 /** 1086 * Sets the master auto-sync setting that applies to all the providers and accounts. 1087 * If this is false then the per-provider auto-sync setting is ignored. 1088 * 1089 * @param sync the master auto-sync setting that applies to all the providers and accounts 1090 */ 1091 public static void setMasterSyncAutomatically(boolean sync) { 1092 try { 1093 getContentService().setMasterSyncAutomatically(sync); 1094 } catch (RemoteException e) { 1095 // exception ignored; if this is thrown then it means the runtime is in the midst of 1096 // being restarted 1097 } 1098 } 1099 1100 /** 1101 * Returns true if there is currently a sync operation for the given 1102 * account or authority in the pending list, or actively being processed. 1103 * @param account the account whose setting we are querying 1104 * @param authority the provider whose behavior is being queried 1105 * @return true if a sync is active for the given account or authority. 1106 */ 1107 public static boolean isSyncActive(Account account, String authority) { 1108 try { 1109 return getContentService().isSyncActive(account, authority); 1110 } catch (RemoteException e) { 1111 throw new RuntimeException("the ContentService should always be reachable", e); 1112 } 1113 } 1114 1115 /** 1116 * If a sync is active returns the information about it, otherwise returns false. 1117 * @return the ActiveSyncInfo for the currently active sync or null if one is not active. 1118 * @hide 1119 */ 1120 public static ActiveSyncInfo getActiveSync() { 1121 try { 1122 return getContentService().getActiveSync(); 1123 } catch (RemoteException e) { 1124 throw new RuntimeException("the ContentService should always be reachable", e); 1125 } 1126 } 1127 1128 /** 1129 * Returns the status that matches the authority. 1130 * @param account the account whose setting we are querying 1131 * @param authority the provider whose behavior is being queried 1132 * @return the SyncStatusInfo for the authority, or null if none exists 1133 * @hide 1134 */ 1135 public static SyncStatusInfo getSyncStatus(Account account, String authority) { 1136 try { 1137 return getContentService().getSyncStatus(account, authority); 1138 } catch (RemoteException e) { 1139 throw new RuntimeException("the ContentService should always be reachable", e); 1140 } 1141 } 1142 1143 /** 1144 * Return true if the pending status is true of any matching authorities. 1145 * @param account the account whose setting we are querying 1146 * @param authority the provider whose behavior is being queried 1147 * @return true if there is a pending sync with the matching account and authority 1148 */ 1149 public static boolean isSyncPending(Account account, String authority) { 1150 try { 1151 return getContentService().isSyncPending(account, authority); 1152 } catch (RemoteException e) { 1153 throw new RuntimeException("the ContentService should always be reachable", e); 1154 } 1155 } 1156 1157 public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) { 1158 try { 1159 ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() { 1160 public void onStatusChanged(int which) throws RemoteException { 1161 callback.onStatusChanged(which); 1162 } 1163 }; 1164 getContentService().addStatusChangeListener(mask, observer); 1165 return observer; 1166 } catch (RemoteException e) { 1167 throw new RuntimeException("the ContentService should always be reachable", e); 1168 } 1169 } 1170 1171 public static void removeStatusChangeListener(Object handle) { 1172 try { 1173 getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle); 1174 } catch (RemoteException e) { 1175 // exception ignored; if this is thrown then it means the runtime is in the midst of 1176 // being restarted 1177 } 1178 } 1179 1180 1181 private final class CursorWrapperInner extends CursorWrapper { 1182 private IContentProvider mContentProvider; 1183 public static final String TAG="CursorWrapperInner"; 1184 private boolean mCloseFlag = false; 1185 1186 CursorWrapperInner(Cursor cursor, IContentProvider icp) { 1187 super(cursor); 1188 mContentProvider = icp; 1189 } 1190 1191 @Override 1192 public void close() { 1193 super.close(); 1194 ContentResolver.this.releaseProvider(mContentProvider); 1195 mCloseFlag = true; 1196 } 1197 1198 @Override 1199 protected void finalize() throws Throwable { 1200 try { 1201 if(!mCloseFlag) { 1202 ContentResolver.this.releaseProvider(mContentProvider); 1203 } 1204 } finally { 1205 super.finalize(); 1206 } 1207 } 1208 } 1209 1210 private final class ParcelFileDescriptorInner extends ParcelFileDescriptor { 1211 private IContentProvider mContentProvider; 1212 public static final String TAG="ParcelFileDescriptorInner"; 1213 private boolean mReleaseProviderFlag = false; 1214 1215 ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) { 1216 super(pfd); 1217 mContentProvider = icp; 1218 } 1219 1220 @Override 1221 public void close() throws IOException { 1222 if(!mReleaseProviderFlag) { 1223 super.close(); 1224 ContentResolver.this.releaseProvider(mContentProvider); 1225 mReleaseProviderFlag = true; 1226 } 1227 } 1228 1229 @Override 1230 protected void finalize() throws Throwable { 1231 if (!mReleaseProviderFlag) { 1232 close(); 1233 } 1234 } 1235 } 1236 1237 /** @hide */ 1238 public static final String CONTENT_SERVICE_NAME = "content"; 1239 1240 /** @hide */ 1241 public static IContentService getContentService() { 1242 if (sContentService != null) { 1243 return sContentService; 1244 } 1245 IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME); 1246 if (Config.LOGV) Log.v("ContentService", "default service binder = " + b); 1247 sContentService = IContentService.Stub.asInterface(b); 1248 if (Config.LOGV) Log.v("ContentService", "default service = " + sContentService); 1249 return sContentService; 1250 } 1251 1252 private static IContentService sContentService; 1253 private final Context mContext; 1254 private static final String TAG = "ContentResolver"; 1255} 1256