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