ContentProvider.java revision 8943737692169f564cd34a9c8d471f3a5d438712
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; 20import android.content.pm.ProviderInfo; 21import android.content.res.AssetFileDescriptor; 22import android.content.res.Configuration; 23import android.database.Cursor; 24import android.database.CursorToBulkCursorAdaptor; 25import android.database.CursorWindow; 26import android.database.IBulkCursor; 27import android.database.IContentObserver; 28import android.database.SQLException; 29import android.net.Uri; 30import android.os.Binder; 31import android.os.ParcelFileDescriptor; 32 33import java.io.File; 34import java.io.FileNotFoundException; 35 36/** 37 * Content providers are one of the primary building blocks of Android applications, providing 38 * content to applications. They encapsulate data and provide it to applications through the single 39 * {@link ContentResolver} interface. A content provider is only required if you need to share 40 * data between multiple applications. For example, the contacts data is used by multiple 41 * applications and must be stored in a content provider. If you don't need to share data amongst 42 * multiple applications you can use a database directly via 43 * {@link android.database.sqlite.SQLiteDatabase}. 44 * 45 * <p>For more information, read <a href="{@docRoot}guide/topics/providers/content-providers.html">Content 46 * Providers</a>.</p> 47 * 48 * <p>When a request is made via 49 * a {@link ContentResolver} the system inspects the authority of the given URI and passes the 50 * request to the content provider registered with the authority. The content provider can interpret 51 * the rest of the URI however it wants. The {@link UriMatcher} class is helpful for parsing 52 * URIs.</p> 53 * 54 * <p>The primary methods that need to be implemented are: 55 * <ul> 56 * <li>{@link #query} which returns data to the caller</li> 57 * <li>{@link #insert} which inserts new data into the content provider</li> 58 * <li>{@link #update} which updates existing data in the content provider</li> 59 * <li>{@link #delete} which deletes data from the content provider</li> 60 * <li>{@link #getType} which returns the MIME type of data in the content provider</li> 61 * </ul></p> 62 * 63 * <p>This class takes care of cross process calls so subclasses don't have to worry about which 64 * process a request is coming from.</p> 65 */ 66public abstract class ContentProvider implements ComponentCallbacks { 67 private Context mContext = null; 68 private String mReadPermission; 69 private String mWritePermission; 70 71 private Transport mTransport = new Transport(); 72 73 /** 74 * Given an IContentProvider, try to coerce it back to the real 75 * ContentProvider object if it is running in the local process. This can 76 * be used if you know you are running in the same process as a provider, 77 * and want to get direct access to its implementation details. Most 78 * clients should not nor have a reason to use it. 79 * 80 * @param abstractInterface The ContentProvider interface that is to be 81 * coerced. 82 * @return If the IContentProvider is non-null and local, returns its actual 83 * ContentProvider instance. Otherwise returns null. 84 * @hide 85 */ 86 public static ContentProvider coerceToLocalContentProvider( 87 IContentProvider abstractInterface) { 88 if (abstractInterface instanceof Transport) { 89 return ((Transport)abstractInterface).getContentProvider(); 90 } 91 return null; 92 } 93 94 /** 95 * Binder object that deals with remoting. 96 * 97 * @hide 98 */ 99 class Transport extends ContentProviderNative { 100 ContentProvider getContentProvider() { 101 return ContentProvider.this; 102 } 103 104 /** 105 * Remote version of a query, which returns an IBulkCursor. The bulk 106 * cursor should be wrapped with BulkCursorToCursorAdaptor before use. 107 */ 108 public IBulkCursor bulkQuery(Uri uri, String[] projection, 109 String selection, String[] selectionArgs, String sortOrder, 110 IContentObserver observer, CursorWindow window) { 111 checkReadPermission(uri); 112 Cursor cursor = ContentProvider.this.query(uri, projection, 113 selection, selectionArgs, sortOrder); 114 if (cursor == null) { 115 return null; 116 } 117 String wperm = getWritePermission(); 118 return new CursorToBulkCursorAdaptor(cursor, observer, 119 ContentProvider.this.getClass().getName(), 120 wperm == null || 121 getContext().checkCallingOrSelfPermission(getWritePermission()) 122 == PackageManager.PERMISSION_GRANTED, 123 window); 124 } 125 126 public Cursor query(Uri uri, String[] projection, 127 String selection, String[] selectionArgs, String sortOrder) { 128 checkReadPermission(uri); 129 return ContentProvider.this.query(uri, projection, selection, 130 selectionArgs, sortOrder); 131 } 132 133 public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs, 134 String sortOrder) { 135 checkReadPermission(uri); 136 return ContentProvider.this.queryEntities(uri, selection, selectionArgs, sortOrder); 137 } 138 139 public String getType(Uri uri) { 140 return ContentProvider.this.getType(uri); 141 } 142 143 144 public Uri insert(Uri uri, ContentValues initialValues) { 145 checkWritePermission(uri); 146 return ContentProvider.this.insert(uri, initialValues); 147 } 148 149 public int bulkInsert(Uri uri, ContentValues[] initialValues) { 150 checkWritePermission(uri); 151 return ContentProvider.this.bulkInsert(uri, initialValues); 152 } 153 154 public Uri insertEntity(Uri uri, Entity entities) { 155 checkWritePermission(uri); 156 return ContentProvider.this.insertEntity(uri, entities); 157 } 158 159 public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) 160 throws OperationApplicationException { 161 for (ContentProviderOperation operation : operations) { 162 if (operation.isReadOperation()) { 163 checkReadPermission(operation.getUri()); 164 } 165 166 if (operation.isWriteOperation()) { 167 checkWritePermission(operation.getUri()); 168 } 169 } 170 return ContentProvider.this.applyBatch(operations); 171 } 172 173 public int delete(Uri uri, String selection, String[] selectionArgs) { 174 checkWritePermission(uri); 175 return ContentProvider.this.delete(uri, selection, selectionArgs); 176 } 177 178 public int update(Uri uri, ContentValues values, String selection, 179 String[] selectionArgs) { 180 checkWritePermission(uri); 181 return ContentProvider.this.update(uri, values, selection, selectionArgs); 182 } 183 184 public int updateEntity(Uri uri, Entity entity) { 185 checkWritePermission(uri); 186 return ContentProvider.this.updateEntity(uri, entity); 187 } 188 189 public ParcelFileDescriptor openFile(Uri uri, String mode) 190 throws FileNotFoundException { 191 if (mode != null && mode.startsWith("rw")) checkWritePermission(uri); 192 else checkReadPermission(uri); 193 return ContentProvider.this.openFile(uri, mode); 194 } 195 196 public AssetFileDescriptor openAssetFile(Uri uri, String mode) 197 throws FileNotFoundException { 198 if (mode != null && mode.startsWith("rw")) checkWritePermission(uri); 199 else checkReadPermission(uri); 200 return ContentProvider.this.openAssetFile(uri, mode); 201 } 202 203 private void checkReadPermission(Uri uri) { 204 final String rperm = getReadPermission(); 205 final int pid = Binder.getCallingPid(); 206 final int uid = Binder.getCallingUid(); 207 if (getContext().checkUriPermission(uri, rperm, null, pid, uid, 208 Intent.FLAG_GRANT_READ_URI_PERMISSION) 209 == PackageManager.PERMISSION_GRANTED) { 210 return; 211 } 212 String msg = "Permission Denial: reading " 213 + ContentProvider.this.getClass().getName() 214 + " uri " + uri + " from pid=" + Binder.getCallingPid() 215 + ", uid=" + Binder.getCallingUid() 216 + " requires " + rperm; 217 throw new SecurityException(msg); 218 } 219 220 private void checkWritePermission(Uri uri) { 221 final String wperm = getWritePermission(); 222 final int pid = Binder.getCallingPid(); 223 final int uid = Binder.getCallingUid(); 224 if (getContext().checkUriPermission(uri, null, wperm, pid, uid, 225 Intent.FLAG_GRANT_WRITE_URI_PERMISSION) 226 == PackageManager.PERMISSION_GRANTED) { 227 return; 228 } 229 String msg = "Permission Denial: writing " 230 + ContentProvider.this.getClass().getName() 231 + " uri " + uri + " from pid=" + Binder.getCallingPid() 232 + ", uid=" + Binder.getCallingUid() 233 + " requires " + wperm; 234 throw new SecurityException(msg); 235 } 236 } 237 238 239 /** 240 * Retrieve the Context this provider is running in. Only available once 241 * onCreate(Map icicle) has been called -- this will be null in the 242 * constructor. 243 */ 244 public final Context getContext() { 245 return mContext; 246 } 247 248 /** 249 * Change the permission required to read data from the content 250 * provider. This is normally set for you from its manifest information 251 * when the provider is first created. 252 * 253 * @param permission Name of the permission required for read-only access. 254 */ 255 protected final void setReadPermission(String permission) { 256 mReadPermission = permission; 257 } 258 259 /** 260 * Return the name of the permission required for read-only access to 261 * this content provider. This method can be called from multiple 262 * threads, as described in 263 * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: 264 * Processes and Threads</a>. 265 */ 266 public final String getReadPermission() { 267 return mReadPermission; 268 } 269 270 /** 271 * Change the permission required to read and write data in the content 272 * provider. This is normally set for you from its manifest information 273 * when the provider is first created. 274 * 275 * @param permission Name of the permission required for read/write access. 276 */ 277 protected final void setWritePermission(String permission) { 278 mWritePermission = permission; 279 } 280 281 /** 282 * Return the name of the permission required for read/write access to 283 * this content provider. This method can be called from multiple 284 * threads, as described in 285 * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: 286 * Processes and Threads</a>. 287 */ 288 public final String getWritePermission() { 289 return mWritePermission; 290 } 291 292 /** 293 * Called when the provider is being started. 294 * 295 * @return true if the provider was successfully loaded, false otherwise 296 */ 297 public abstract boolean onCreate(); 298 299 public void onConfigurationChanged(Configuration newConfig) { 300 } 301 302 public void onLowMemory() { 303 } 304 305 /** 306 * Receives a query request from a client in a local process, and 307 * returns a Cursor. This is called internally by the {@link ContentResolver}. 308 * This method can be called from multiple 309 * threads, as described in 310 * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: 311 * Processes and Threads</a>. 312 * <p> 313 * Example client call:<p> 314 * <pre>// Request a specific record. 315 * Cursor managedCursor = managedQuery( 316 Contacts.People.CONTENT_URI.addId(2), 317 projection, // Which columns to return. 318 null, // WHERE clause. 319 People.NAME + " ASC"); // Sort order.</pre> 320 * Example implementation:<p> 321 * <pre>// SQLiteQueryBuilder is a helper class that creates the 322 // proper SQL syntax for us. 323 SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder(); 324 325 // Set the table we're querying. 326 qBuilder.setTables(DATABASE_TABLE_NAME); 327 328 // If the query ends in a specific record number, we're 329 // being asked for a specific record, so set the 330 // WHERE clause in our query. 331 if((URI_MATCHER.match(uri)) == SPECIFIC_MESSAGE){ 332 qBuilder.appendWhere("_id=" + uri.getPathLeafId()); 333 } 334 335 // Make the query. 336 Cursor c = qBuilder.query(mDb, 337 projection, 338 selection, 339 selectionArgs, 340 groupBy, 341 having, 342 sortOrder); 343 c.setNotificationUri(getContext().getContentResolver(), uri); 344 return c;</pre> 345 * 346 * @param uri The URI to query. This will be the full URI sent by the client; 347 * if the client is requesting a specific record, the URI will end in a record number 348 * that the implementation should parse and add to a WHERE or HAVING clause, specifying 349 * that _id value. 350 * @param projection The list of columns to put into the cursor. If 351 * null all columns are included. 352 * @param selection A selection criteria to apply when filtering rows. 353 * If null then all rows are included. 354 * @param sortOrder How the rows in the cursor should be sorted. 355 * If null then the provider is free to define the sort order. 356 * @return a Cursor or null. 357 */ 358 public abstract Cursor query(Uri uri, String[] projection, 359 String selection, String[] selectionArgs, String sortOrder); 360 361 public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs, 362 String sortOrder) { 363 throw new UnsupportedOperationException(); 364 } 365 366 /** 367 * Return the MIME type of the data at the given URI. This should start with 368 * <code>vnd.android.cursor.item</code> for a single record, 369 * or <code>vnd.android.cursor.dir/</code> for multiple items. 370 * This method can be called from multiple 371 * threads, as described in 372 * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: 373 * Processes and Threads</a>. 374 * 375 * @param uri the URI to query. 376 * @return a MIME type string, or null if there is no type. 377 */ 378 public abstract String getType(Uri uri); 379 380 /** 381 * Implement this to insert a new row. 382 * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} 383 * after inserting. 384 * This method can be called from multiple 385 * threads, as described in 386 * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: 387 * Processes and Threads</a>. 388 * @param uri The content:// URI of the insertion request. 389 * @param values A set of column_name/value pairs to add to the database. 390 * @return The URI for the newly inserted item. 391 */ 392 public abstract Uri insert(Uri uri, ContentValues values); 393 394 /** 395 * Implement this to insert a set of new rows, or the default implementation will 396 * iterate over the values and call {@link #insert} on each of them. 397 * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} 398 * after inserting. 399 * This method can be called from multiple 400 * threads, as described in 401 * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: 402 * Processes and Threads</a>. 403 * 404 * @param uri The content:// URI of the insertion request. 405 * @param values An array of sets of column_name/value pairs to add to the database. 406 * @return The number of values that were inserted. 407 */ 408 public int bulkInsert(Uri uri, ContentValues[] values) { 409 int numValues = values.length; 410 for (int i = 0; i < numValues; i++) { 411 insert(uri, values[i]); 412 } 413 return numValues; 414 } 415 416 public Uri insertEntity(Uri uri, Entity entity) { 417 throw new UnsupportedOperationException(); 418 } 419 420 /** 421 * A request to delete one or more rows. The selection clause is applied when performing 422 * the deletion, allowing the operation to affect multiple rows in a 423 * directory. 424 * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()} 425 * after deleting. 426 * This method can be called from multiple 427 * threads, as described in 428 * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: 429 * Processes and Threads</a>. 430 * 431 * <p>The implementation is responsible for parsing out a row ID at the end 432 * of the URI, if a specific row is being deleted. That is, the client would 433 * pass in <code>content://contacts/people/22</code> and the implementation is 434 * responsible for parsing the record number (22) when creating a SQL statement. 435 * 436 * @param uri The full URI to query, including a row ID (if a specific record is requested). 437 * @param selection An optional restriction to apply to rows when deleting. 438 * @return The number of rows affected. 439 * @throws SQLException 440 */ 441 public abstract int delete(Uri uri, String selection, String[] selectionArgs); 442 443 /** 444 * Update a content URI. All rows matching the optionally provided selection 445 * will have their columns listed as the keys in the values map with the 446 * values of those keys. 447 * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} 448 * after updating. 449 * This method can be called from multiple 450 * threads, as described in 451 * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: 452 * Processes and Threads</a>. 453 * 454 * @param uri The URI to query. This can potentially have a record ID if this 455 * is an update request for a specific record. 456 * @param values A Bundle mapping from column names to new column values (NULL is a 457 * valid value). 458 * @param selection An optional filter to match rows to update. 459 * @return the number of rows affected. 460 */ 461 public abstract int update(Uri uri, ContentValues values, String selection, 462 String[] selectionArgs); 463 464 public int updateEntity(Uri uri, Entity entity) { 465 throw new UnsupportedOperationException(); 466 } 467 468 /** 469 * Open a file blob associated with a content URI. 470 * This method can be called from multiple 471 * threads, as described in 472 * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: 473 * Processes and Threads</a>. 474 * 475 * <p>Returns a 476 * ParcelFileDescriptor, from which you can obtain a 477 * {@link java.io.FileDescriptor} for use with 478 * {@link java.io.FileInputStream}, {@link java.io.FileOutputStream}, etc. 479 * This can be used to store large data (such as an image) associated with 480 * a particular piece of content. 481 * 482 * <p>The returned ParcelFileDescriptor is owned by the caller, so it is 483 * their responsibility to close it when done. That is, the implementation 484 * of this method should create a new ParcelFileDescriptor for each call. 485 * 486 * @param uri The URI whose file is to be opened. 487 * @param mode Access mode for the file. May be "r" for read-only access, 488 * "rw" for read and write access, or "rwt" for read and write access 489 * that truncates any existing file. 490 * 491 * @return Returns a new ParcelFileDescriptor which you can use to access 492 * the file. 493 * 494 * @throws FileNotFoundException Throws FileNotFoundException if there is 495 * no file associated with the given URI or the mode is invalid. 496 * @throws SecurityException Throws SecurityException if the caller does 497 * not have permission to access the file. 498 * 499 * @see #openAssetFile(Uri, String) 500 * @see #openFileHelper(Uri, String) 501 */ 502 public ParcelFileDescriptor openFile(Uri uri, String mode) 503 throws FileNotFoundException { 504 throw new FileNotFoundException("No files supported by provider at " 505 + uri); 506 } 507 508 /** 509 * This is like {@link #openFile}, but can be implemented by providers 510 * that need to be able to return sub-sections of files, often assets 511 * inside of their .apk. Note that when implementing this your clients 512 * must be able to deal with such files, either directly with 513 * {@link ContentResolver#openAssetFileDescriptor 514 * ContentResolver.openAssetFileDescriptor}, or by using the higher-level 515 * {@link ContentResolver#openInputStream ContentResolver.openInputStream} 516 * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream} 517 * methods. 518 * 519 * <p><em>Note: if you are implementing this to return a full file, you 520 * should create the AssetFileDescriptor with 521 * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with 522 * applications that can not handle sub-sections of files.</em></p> 523 * 524 * @param uri The URI whose file is to be opened. 525 * @param mode Access mode for the file. May be "r" for read-only access, 526 * "w" for write-only access (erasing whatever data is currently in 527 * the file), "wa" for write-only access to append to any existing data, 528 * "rw" for read and write access on any existing data, and "rwt" for read 529 * and write access that truncates any existing file. 530 * 531 * @return Returns a new AssetFileDescriptor which you can use to access 532 * the file. 533 * 534 * @throws FileNotFoundException Throws FileNotFoundException if there is 535 * no file associated with the given URI or the mode is invalid. 536 * @throws SecurityException Throws SecurityException if the caller does 537 * not have permission to access the file. 538 * 539 * @see #openFile(Uri, String) 540 * @see #openFileHelper(Uri, String) 541 */ 542 public AssetFileDescriptor openAssetFile(Uri uri, String mode) 543 throws FileNotFoundException { 544 ParcelFileDescriptor fd = openFile(uri, mode); 545 return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; 546 } 547 548 /** 549 * Convenience for subclasses that wish to implement {@link #openFile} 550 * by looking up a column named "_data" at the given URI. 551 * 552 * @param uri The URI to be opened. 553 * @param mode The file mode. May be "r" for read-only access, 554 * "w" for write-only access (erasing whatever data is currently in 555 * the file), "wa" for write-only access to append to any existing data, 556 * "rw" for read and write access on any existing data, and "rwt" for read 557 * and write access that truncates any existing file. 558 * 559 * @return Returns a new ParcelFileDescriptor that can be used by the 560 * client to access the file. 561 */ 562 protected final ParcelFileDescriptor openFileHelper(Uri uri, 563 String mode) throws FileNotFoundException { 564 Cursor c = query(uri, new String[]{"_data"}, null, null, null); 565 int count = (c != null) ? c.getCount() : 0; 566 if (count != 1) { 567 // If there is not exactly one result, throw an appropriate 568 // exception. 569 if (c != null) { 570 c.close(); 571 } 572 if (count == 0) { 573 throw new FileNotFoundException("No entry for " + uri); 574 } 575 throw new FileNotFoundException("Multiple items at " + uri); 576 } 577 578 c.moveToFirst(); 579 int i = c.getColumnIndex("_data"); 580 String path = (i >= 0 ? c.getString(i) : null); 581 c.close(); 582 if (path == null) { 583 throw new FileNotFoundException("Column _data not found."); 584 } 585 586 int modeBits = ContentResolver.modeToMode(uri, mode); 587 return ParcelFileDescriptor.open(new File(path), modeBits); 588 } 589 590 /** 591 * Returns true if this instance is a temporary content provider. 592 * @return true if this instance is a temporary content provider 593 */ 594 protected boolean isTemporary() { 595 return false; 596 } 597 598 /** 599 * Returns the Binder object for this provider. 600 * 601 * @return the Binder object for this provider 602 * @hide 603 */ 604 public IContentProvider getIContentProvider() { 605 return mTransport; 606 } 607 608 /** 609 * After being instantiated, this is called to tell the content provider 610 * about itself. 611 * 612 * @param context The context this provider is running in 613 * @param info Registered information about this content provider 614 */ 615 public void attachInfo(Context context, ProviderInfo info) { 616 617 /* 618 * Only allow it to be set once, so after the content service gives 619 * this to us clients can't change it. 620 */ 621 if (mContext == null) { 622 mContext = context; 623 if (info != null) { 624 setReadPermission(info.readPermission); 625 setWritePermission(info.writePermission); 626 } 627 ContentProvider.this.onCreate(); 628 } 629 } 630 631 /** 632 * Applies each of the {@link ContentProviderOperation} objects and returns an array 633 * of their results. Passes through OperationApplicationException, which may be thrown 634 * by the call to {@link ContentProviderOperation#apply}. 635 * If all the applications succeed then a {@link ContentProviderResult} array with the 636 * same number of elements as the operations will be returned. It is implementation-specific 637 * how many, if any, operations will have been successfully applied if a call to 638 * apply results in a {@link OperationApplicationException}. 639 * @param operations the operations to apply 640 * @return the results of the applications 641 * @throws OperationApplicationException thrown if an application fails. 642 * See {@link ContentProviderOperation#apply} for more information. 643 */ 644 public ContentProviderResult[] applyBatch(ContentProviderOperation[] operations) 645 throws OperationApplicationException { 646 ContentProviderResult[] results = new ContentProviderResult[operations.length]; 647 for (int i = 0; i < operations.length; i++) { 648 results[i] = operations[i].apply(this, results, i); 649 } 650 return results; 651 } 652}