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