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