ContentResolver.java revision 77709755b74bcc852cd511ff833c2827c0f0e1aa
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 class OpenResourceIdResult { 531 Resources r; 532 int id; 533 } 534 535 OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException { 536 String authority = uri.getAuthority(); 537 Resources r; 538 if (TextUtils.isEmpty(authority)) { 539 throw new FileNotFoundException("No authority: " + uri); 540 } else { 541 try { 542 r = mContext.getPackageManager().getResourcesForApplication(authority); 543 } catch (NameNotFoundException ex) { 544 throw new FileNotFoundException("No package found for authority: " + uri); 545 } 546 } 547 List<String> path = uri.getPathSegments(); 548 if (path == null) { 549 throw new FileNotFoundException("No path: " + uri); 550 } 551 int len = path.size(); 552 int id; 553 if (len == 1) { 554 try { 555 id = Integer.parseInt(path.get(0)); 556 } catch (NumberFormatException e) { 557 throw new FileNotFoundException("Single path segment is not a resource ID: " + uri); 558 } 559 } else if (len == 2) { 560 id = r.getIdentifier(path.get(1), path.get(0), authority); 561 } else { 562 throw new FileNotFoundException("More than two path segments: " + uri); 563 } 564 if (id == 0) { 565 throw new FileNotFoundException("No resource found for: " + uri); 566 } 567 OpenResourceIdResult res = new OpenResourceIdResult(); 568 res.r = r; 569 res.id = id; 570 return res; 571 } 572 573 /** @hide */ 574 static public int modeToMode(Uri uri, String mode) throws FileNotFoundException { 575 int modeBits; 576 if ("r".equals(mode)) { 577 modeBits = ParcelFileDescriptor.MODE_READ_ONLY; 578 } else if ("w".equals(mode) || "wt".equals(mode)) { 579 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY 580 | ParcelFileDescriptor.MODE_CREATE 581 | ParcelFileDescriptor.MODE_TRUNCATE; 582 } else if ("wa".equals(mode)) { 583 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY 584 | ParcelFileDescriptor.MODE_CREATE 585 | ParcelFileDescriptor.MODE_APPEND; 586 } else if ("rw".equals(mode)) { 587 modeBits = ParcelFileDescriptor.MODE_READ_WRITE 588 | ParcelFileDescriptor.MODE_CREATE; 589 } else if ("rwt".equals(mode)) { 590 modeBits = ParcelFileDescriptor.MODE_READ_WRITE 591 | ParcelFileDescriptor.MODE_CREATE 592 | ParcelFileDescriptor.MODE_TRUNCATE; 593 } else { 594 throw new FileNotFoundException("Bad mode for " + uri + ": " 595 + mode); 596 } 597 return modeBits; 598 } 599 600 /** 601 * Inserts a row into a table at the given URL. 602 * 603 * If the content provider supports transactions the insertion will be atomic. 604 * 605 * @param url The URL of the table to insert into. 606 * @param values The initial values for the newly inserted row. The key is the column name for 607 * the field. Passing an empty ContentValues will create an empty row. 608 * @return the URL of the newly created row. 609 */ 610 public final Uri insert(Uri url, ContentValues values) 611 { 612 IContentProvider provider = acquireProvider(url); 613 if (provider == null) { 614 throw new IllegalArgumentException("Unknown URL " + url); 615 } 616 try { 617 return provider.insert(url, values); 618 } catch (RemoteException e) { 619 return null; 620 } finally { 621 releaseProvider(provider); 622 } 623 } 624 625 /** 626 * Applies each of the {@link ContentProviderOperation} objects and returns an array 627 * of their results. Passes through OperationApplicationException, which may be thrown 628 * by the call to {@link ContentProviderOperation#apply}. 629 * If all the applications succeed then a {@link ContentProviderResult} array with the 630 * same number of elements as the operations will be returned. It is implementation-specific 631 * how many, if any, operations will have been successfully applied if a call to 632 * apply results in a {@link OperationApplicationException}. 633 * @param authority the authority of the ContentProvider to which this batch should be applied 634 * @param operations the operations to apply 635 * @return the results of the applications 636 * @throws OperationApplicationException thrown if an application fails. 637 * See {@link ContentProviderOperation#apply} for more information. 638 * @throws RemoteException thrown if a RemoteException is encountered while attempting 639 * to communicate with a remote provider. 640 */ 641 public ContentProviderResult[] applyBatch(String authority, 642 ArrayList<ContentProviderOperation> operations) 643 throws RemoteException, OperationApplicationException { 644 ContentProviderClient provider = acquireContentProviderClient(authority); 645 if (provider == null) { 646 throw new IllegalArgumentException("Unknown authority " + authority); 647 } 648 try { 649 return provider.applyBatch(operations); 650 } finally { 651 provider.release(); 652 } 653 } 654 655 /** 656 * Inserts multiple rows into a table at the given URL. 657 * 658 * This function make no guarantees about the atomicity of the insertions. 659 * 660 * @param url The URL of the table to insert into. 661 * @param values The initial values for the newly inserted rows. The key is the column name for 662 * the field. Passing null will create an empty row. 663 * @return the number of newly created rows. 664 */ 665 public final int bulkInsert(Uri url, ContentValues[] values) 666 { 667 IContentProvider provider = acquireProvider(url); 668 if (provider == null) { 669 throw new IllegalArgumentException("Unknown URL " + url); 670 } 671 try { 672 return provider.bulkInsert(url, values); 673 } catch (RemoteException e) { 674 return 0; 675 } finally { 676 releaseProvider(provider); 677 } 678 } 679 680 /** 681 * Deletes row(s) specified by a content URI. 682 * 683 * If the content provider supports transactions, the deletion will be atomic. 684 * 685 * @param url The URL of the row to delete. 686 * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause 687 (excluding the WHERE itself). 688 * @return The number of rows deleted. 689 */ 690 public final int delete(Uri url, String where, String[] selectionArgs) 691 { 692 IContentProvider provider = acquireProvider(url); 693 if (provider == null) { 694 throw new IllegalArgumentException("Unknown URL " + url); 695 } 696 try { 697 return provider.delete(url, where, selectionArgs); 698 } catch (RemoteException e) { 699 return -1; 700 } finally { 701 releaseProvider(provider); 702 } 703 } 704 705 /** 706 * Update row(s) in a content URI. 707 * 708 * If the content provider supports transactions the update will be atomic. 709 * 710 * @param uri The URI to modify. 711 * @param values The new field values. The key is the column name for the field. 712 A null value will remove an existing field value. 713 * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause 714 (excluding the WHERE itself). 715 * @return The number of rows updated. 716 * @throws NullPointerException if uri or values are null 717 */ 718 public final int update(Uri uri, ContentValues values, String where, 719 String[] selectionArgs) { 720 IContentProvider provider = acquireProvider(uri); 721 if (provider == null) { 722 throw new IllegalArgumentException("Unknown URI " + uri); 723 } 724 try { 725 return provider.update(uri, values, where, selectionArgs); 726 } catch (RemoteException e) { 727 return -1; 728 } finally { 729 releaseProvider(provider); 730 } 731 } 732 733 /** 734 * Returns the content provider for the given content URI.. 735 * 736 * @param uri The URI to a content provider 737 * @return The ContentProvider for the given URI, or null if no content provider is found. 738 * @hide 739 */ 740 public final IContentProvider acquireProvider(Uri uri) 741 { 742 if (!SCHEME_CONTENT.equals(uri.getScheme())) { 743 return null; 744 } 745 String auth = uri.getAuthority(); 746 if (auth != null) { 747 return acquireProvider(mContext, uri.getAuthority()); 748 } 749 return null; 750 } 751 752 /** 753 * @hide 754 */ 755 public final IContentProvider acquireProvider(String name) { 756 if(name == null) { 757 return null; 758 } 759 return acquireProvider(mContext, name); 760 } 761 762 /** 763 * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 764 * that services the content at uri, starting the provider if necessary. Returns 765 * null if there is no provider associated wih the uri. The caller must indicate that they are 766 * done with the provider by calling {@link ContentProviderClient#release} which will allow 767 * the system to release the provider it it determines that there is no other reason for 768 * keeping it active. 769 * @param uri specifies which provider should be acquired 770 * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 771 * that services the content at uri or null if there isn't one. 772 */ 773 public final ContentProviderClient acquireContentProviderClient(Uri uri) { 774 IContentProvider provider = acquireProvider(uri); 775 if (provider != null) { 776 return new ContentProviderClient(this, provider); 777 } 778 779 return null; 780 } 781 782 /** 783 * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 784 * with the authority of name, starting the provider if necessary. Returns 785 * null if there is no provider associated wih the uri. The caller must indicate that they are 786 * done with the provider by calling {@link ContentProviderClient#release} which will allow 787 * the system to release the provider it it determines that there is no other reason for 788 * keeping it active. 789 * @param name specifies which provider should be acquired 790 * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider} 791 * with the authority of name or null if there isn't one. 792 */ 793 public final ContentProviderClient acquireContentProviderClient(String name) { 794 IContentProvider provider = acquireProvider(name); 795 if (provider != null) { 796 return new ContentProviderClient(this, provider); 797 } 798 799 return null; 800 } 801 802 /** 803 * Register an observer class that gets callbacks when data identified by a 804 * given content URI changes. 805 * 806 * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI 807 * for a whole class of content. 808 * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code> 809 * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI 810 * specified by <em>uri</em> will cause notifications to be sent. If true, than any URI values 811 * at or below the specified URI will also trigger a match. 812 * @param observer The object that receives callbacks when changes occur. 813 * @see #unregisterContentObserver 814 */ 815 public final void registerContentObserver(Uri uri, boolean notifyForDescendents, 816 ContentObserver observer) 817 { 818 try { 819 getContentService().registerContentObserver(uri, notifyForDescendents, 820 observer.getContentObserver()); 821 } catch (RemoteException e) { 822 } 823 } 824 825 /** 826 * Unregisters a change observer. 827 * 828 * @param observer The previously registered observer that is no longer needed. 829 * @see #registerContentObserver 830 */ 831 public final void unregisterContentObserver(ContentObserver observer) { 832 try { 833 IContentObserver contentObserver = observer.releaseContentObserver(); 834 if (contentObserver != null) { 835 getContentService().unregisterContentObserver( 836 contentObserver); 837 } 838 } catch (RemoteException e) { 839 } 840 } 841 842 /** 843 * Notify registered observers that a row was updated. 844 * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}. 845 * By default, CursorAdapter objects will get this notification. 846 * 847 * @param uri 848 * @param observer The observer that originated the change, may be <code>null</null> 849 */ 850 public void notifyChange(Uri uri, ContentObserver observer) { 851 notifyChange(uri, observer, true /* sync to network */); 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 * @param syncToNetwork If true, attempt to sync the change to the network. 862 */ 863 public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { 864 try { 865 getContentService().notifyChange( 866 uri, observer == null ? null : observer.getContentObserver(), 867 observer != null && observer.deliverSelfNotifications(), syncToNetwork); 868 } catch (RemoteException e) { 869 } 870 } 871 872 /** 873 * Start an asynchronous sync operation. If you want to monitor the progress 874 * of the sync you may register a SyncObserver. Only values of the following 875 * types may be used in the extras bundle: 876 * <ul> 877 * <li>Integer</li> 878 * <li>Long</li> 879 * <li>Boolean</li> 880 * <li>Float</li> 881 * <li>Double</li> 882 * <li>String</li> 883 * </ul> 884 * 885 * @param uri the uri of the provider to sync or null to sync all providers. 886 * @param extras any extras to pass to the SyncAdapter. 887 * @deprecated instead use 888 * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)} 889 */ 890 public void startSync(Uri uri, Bundle extras) { 891 Account account = null; 892 if (extras != null) { 893 String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT); 894 if (!TextUtils.isEmpty(accountName)) { 895 account = new Account(accountName, "com.google.GAIA"); 896 } 897 extras.remove(SYNC_EXTRAS_ACCOUNT); 898 } 899 requestSync(account, uri != null ? uri.getAuthority() : null, extras); 900 } 901 902 /** 903 * Start an asynchronous sync operation. If you want to monitor the progress 904 * of the sync you may register a SyncObserver. Only values of the following 905 * types may be used in the extras bundle: 906 * <ul> 907 * <li>Integer</li> 908 * <li>Long</li> 909 * <li>Boolean</li> 910 * <li>Float</li> 911 * <li>Double</li> 912 * <li>String</li> 913 * </ul> 914 * 915 * @param account which account should be synced 916 * @param authority which authority should be synced 917 * @param extras any extras to pass to the SyncAdapter. 918 */ 919 public static void requestSync(Account account, String authority, Bundle extras) { 920 validateSyncExtrasBundle(extras); 921 try { 922 getContentService().requestSync(account, authority, extras); 923 } catch (RemoteException e) { 924 } 925 } 926 927 /** 928 * Check that only values of the following types are in the Bundle: 929 * <ul> 930 * <li>Integer</li> 931 * <li>Long</li> 932 * <li>Boolean</li> 933 * <li>Float</li> 934 * <li>Double</li> 935 * <li>String</li> 936 * <li>Account</li> 937 * <li>null</li> 938 * </ul> 939 * @param extras the Bundle to check 940 */ 941 public static void validateSyncExtrasBundle(Bundle extras) { 942 try { 943 for (String key : extras.keySet()) { 944 Object value = extras.get(key); 945 if (value == null) continue; 946 if (value instanceof Long) continue; 947 if (value instanceof Integer) continue; 948 if (value instanceof Boolean) continue; 949 if (value instanceof Float) continue; 950 if (value instanceof Double) continue; 951 if (value instanceof String) continue; 952 if (value instanceof Account) continue; 953 throw new IllegalArgumentException("unexpected value type: " 954 + value.getClass().getName()); 955 } 956 } catch (IllegalArgumentException e) { 957 throw e; 958 } catch (RuntimeException exc) { 959 throw new IllegalArgumentException("error unparceling Bundle", exc); 960 } 961 } 962 963 /** 964 * Cancel any active or pending syncs that match the Uri. If the uri is null then 965 * all syncs will be canceled. 966 * 967 * @param uri the uri of the provider to sync or null to sync all providers. 968 * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)} 969 */ 970 public void cancelSync(Uri uri) { 971 cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null); 972 } 973 974 /** 975 * Cancel any active or pending syncs that match account and authority. The account and 976 * authority can each independently be set to null, which means that syncs with any account 977 * or authority, respectively, will match. 978 * 979 * @param account filters the syncs that match by this account 980 * @param authority filters the syncs that match by this authority 981 */ 982 public static void cancelSync(Account account, String authority) { 983 try { 984 getContentService().cancelSync(account, authority); 985 } catch (RemoteException e) { 986 } 987 } 988 989 /** 990 * Get information about the SyncAdapters that are known to the system. 991 * @return an array of SyncAdapters that have registered with the system 992 */ 993 public static SyncAdapterType[] getSyncAdapterTypes() { 994 try { 995 return getContentService().getSyncAdapterTypes(); 996 } catch (RemoteException e) { 997 throw new RuntimeException("the ContentService should always be reachable", e); 998 } 999 } 1000 1001 /** 1002 * Check if the provider should be synced when a network tickle is received 1003 * 1004 * @param account the account whose setting we are querying 1005 * @param authority the provider whose setting we are querying 1006 * @return true if the provider should be synced when a network tickle is received 1007 */ 1008 public static boolean getSyncAutomatically(Account account, String authority) { 1009 try { 1010 return getContentService().getSyncAutomatically(account, authority); 1011 } catch (RemoteException e) { 1012 throw new RuntimeException("the ContentService should always be reachable", e); 1013 } 1014 } 1015 1016 /** 1017 * Set whether or not the provider is synced when it receives a network tickle. 1018 * 1019 * @param account the account whose setting we are querying 1020 * @param authority the provider whose behavior is being controlled 1021 * @param sync true if the provider should be synced when tickles are received for it 1022 */ 1023 public static void setSyncAutomatically(Account account, String authority, boolean sync) { 1024 try { 1025 getContentService().setSyncAutomatically(account, authority, sync); 1026 } catch (RemoteException e) { 1027 // exception ignored; if this is thrown then it means the runtime is in the midst of 1028 // being restarted 1029 } 1030 } 1031 1032 /** 1033 * Check if this account/provider is syncable. 1034 * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet. 1035 */ 1036 public static int getIsSyncable(Account account, String authority) { 1037 try { 1038 return getContentService().getIsSyncable(account, authority); 1039 } catch (RemoteException e) { 1040 throw new RuntimeException("the ContentService should always be reachable", e); 1041 } 1042 } 1043 1044 /** 1045 * Set whether this account/provider is syncable. 1046 * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown 1047 */ 1048 public static void setIsSyncable(Account account, String authority, int syncable) { 1049 try { 1050 getContentService().setIsSyncable(account, authority, syncable); 1051 } catch (RemoteException e) { 1052 // exception ignored; if this is thrown then it means the runtime is in the midst of 1053 // being restarted 1054 } 1055 } 1056 1057 /** 1058 * Gets the master auto-sync setting that applies to all the providers and accounts. 1059 * If this is false then the per-provider auto-sync setting is ignored. 1060 * 1061 * @return the master auto-sync setting that applies to all the providers and accounts 1062 */ 1063 public static boolean getMasterSyncAutomatically() { 1064 try { 1065 return getContentService().getMasterSyncAutomatically(); 1066 } catch (RemoteException e) { 1067 throw new RuntimeException("the ContentService should always be reachable", e); 1068 } 1069 } 1070 1071 /** 1072 * Sets 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 * @param sync the master auto-sync setting that applies to all the providers and accounts 1076 */ 1077 public static void setMasterSyncAutomatically(boolean sync) { 1078 try { 1079 getContentService().setMasterSyncAutomatically(sync); 1080 } catch (RemoteException e) { 1081 // exception ignored; if this is thrown then it means the runtime is in the midst of 1082 // being restarted 1083 } 1084 } 1085 1086 /** 1087 * Returns true if there is currently a sync operation for the given 1088 * account or authority in the pending list, or actively being processed. 1089 * @param account the account whose setting we are querying 1090 * @param authority the provider whose behavior is being queried 1091 * @return true if a sync is active for the given account or authority. 1092 */ 1093 public static boolean isSyncActive(Account account, String authority) { 1094 try { 1095 return getContentService().isSyncActive(account, authority); 1096 } catch (RemoteException e) { 1097 throw new RuntimeException("the ContentService should always be reachable", e); 1098 } 1099 } 1100 1101 /** 1102 * If a sync is active returns the information about it, otherwise returns false. 1103 * @return the ActiveSyncInfo for the currently active sync or null if one is not active. 1104 * @hide 1105 */ 1106 public static ActiveSyncInfo getActiveSync() { 1107 try { 1108 return getContentService().getActiveSync(); 1109 } catch (RemoteException e) { 1110 throw new RuntimeException("the ContentService should always be reachable", e); 1111 } 1112 } 1113 1114 /** 1115 * Returns the status that matches the authority. 1116 * @param account the account whose setting we are querying 1117 * @param authority the provider whose behavior is being queried 1118 * @return the SyncStatusInfo for the authority, or null if none exists 1119 * @hide 1120 */ 1121 public static SyncStatusInfo getSyncStatus(Account account, String authority) { 1122 try { 1123 return getContentService().getSyncStatus(account, authority); 1124 } catch (RemoteException e) { 1125 throw new RuntimeException("the ContentService should always be reachable", e); 1126 } 1127 } 1128 1129 /** 1130 * Return true if the pending status is true of any matching authorities. 1131 * @param account the account whose setting we are querying 1132 * @param authority the provider whose behavior is being queried 1133 * @return true if there is a pending sync with the matching account and authority 1134 */ 1135 public static boolean isSyncPending(Account account, String authority) { 1136 try { 1137 return getContentService().isSyncPending(account, authority); 1138 } catch (RemoteException e) { 1139 throw new RuntimeException("the ContentService should always be reachable", e); 1140 } 1141 } 1142 1143 public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) { 1144 try { 1145 ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() { 1146 public void onStatusChanged(int which) throws RemoteException { 1147 callback.onStatusChanged(which); 1148 } 1149 }; 1150 getContentService().addStatusChangeListener(mask, observer); 1151 return observer; 1152 } catch (RemoteException e) { 1153 throw new RuntimeException("the ContentService should always be reachable", e); 1154 } 1155 } 1156 1157 public static void removeStatusChangeListener(Object handle) { 1158 try { 1159 getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle); 1160 } catch (RemoteException e) { 1161 // exception ignored; if this is thrown then it means the runtime is in the midst of 1162 // being restarted 1163 } 1164 } 1165 1166 1167 private final class CursorWrapperInner extends CursorWrapper { 1168 private IContentProvider mContentProvider; 1169 public static final String TAG="CursorWrapperInner"; 1170 private boolean mCloseFlag = false; 1171 1172 CursorWrapperInner(Cursor cursor, IContentProvider icp) { 1173 super(cursor); 1174 mContentProvider = icp; 1175 } 1176 1177 @Override 1178 public void close() { 1179 super.close(); 1180 ContentResolver.this.releaseProvider(mContentProvider); 1181 mCloseFlag = true; 1182 } 1183 1184 @Override 1185 protected void finalize() throws Throwable { 1186 try { 1187 if(!mCloseFlag) { 1188 ContentResolver.this.releaseProvider(mContentProvider); 1189 } 1190 } finally { 1191 super.finalize(); 1192 } 1193 } 1194 } 1195 1196 private final class ParcelFileDescriptorInner extends ParcelFileDescriptor { 1197 private IContentProvider mContentProvider; 1198 public static final String TAG="ParcelFileDescriptorInner"; 1199 private boolean mReleaseProviderFlag = false; 1200 1201 ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) { 1202 super(pfd); 1203 mContentProvider = icp; 1204 } 1205 1206 @Override 1207 public void close() throws IOException { 1208 if(!mReleaseProviderFlag) { 1209 super.close(); 1210 ContentResolver.this.releaseProvider(mContentProvider); 1211 mReleaseProviderFlag = true; 1212 } 1213 } 1214 1215 @Override 1216 protected void finalize() throws Throwable { 1217 if (!mReleaseProviderFlag) { 1218 close(); 1219 } 1220 } 1221 } 1222 1223 /** @hide */ 1224 public static final String CONTENT_SERVICE_NAME = "content"; 1225 1226 /** @hide */ 1227 public static IContentService getContentService() { 1228 if (sContentService != null) { 1229 return sContentService; 1230 } 1231 IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME); 1232 if (Config.LOGV) Log.v("ContentService", "default service binder = " + b); 1233 sContentService = IContentService.Stub.asInterface(b); 1234 if (Config.LOGV) Log.v("ContentService", "default service = " + sContentService); 1235 return sContentService; 1236 } 1237 1238 private static IContentService sContentService; 1239 private final Context mContext; 1240 private static final String TAG = "ContentResolver"; 1241} 1242