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}