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