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