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