alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Android example source code file (ContentProvider.java)

This example Android source code file (ContentProvider.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Android by Example" TM.

Java - Android tags/keywords

android, assetfiledescriptor, bundle, content, contentprovider, contentproviderresult, contentvalues, context, cursor, database, filenotfoundexception, io, network, operationapplicationexception, os, parcelfiledescriptor, pathpermission, string, transport, uri

The ContentProvider.java Android example source code

/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.content;

import android.content.pm.PackageManager;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
import android.database.Cursor;
import android.database.CursorToBulkCursorAdaptor;
import android.database.CursorWindow;
import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.database.SQLException;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;

/**
 * Content providers are one of the primary building blocks of Android applications, providing
 * content to applications. They encapsulate data and provide it to applications through the single
 * {@link ContentResolver} interface. A content provider is only required if you need to share
 * data between multiple applications. For example, the contacts data is used by multiple
 * applications and must be stored in a content provider. If you don't need to share data amongst
 * multiple applications you can use a database directly via
 * {@link android.database.sqlite.SQLiteDatabase}.
 *
 * <p>For more information, read Content
 * Providers</a>.

* * <p>When a request is made via * a {@link ContentResolver} the system inspects the authority of the given URI and passes the * request to the content provider registered with the authority. The content provider can interpret * the rest of the URI however it wants. The {@link UriMatcher} class is helpful for parsing * URIs.</p> * * <p>The primary methods that need to be implemented are: * <ul> * <li>{@link #query} which returns data to the caller * <li>{@link #insert} which inserts new data into the content provider * <li>{@link #update} which updates existing data in the content provider * <li>{@link #delete} which deletes data from the content provider * <li>{@link #getType} which returns the MIME type of data in the content provider * </ul>

* * <p>This class takes care of cross process calls so subclasses don't have to worry about which * process a request is coming from.</p> */ public abstract class ContentProvider implements ComponentCallbacks { /* * Note: if you add methods to ContentProvider, you must add similar methods to * MockContentProvider. */ private Context mContext = null; private int mMyUid; private String mReadPermission; private String mWritePermission; private PathPermission[] mPathPermissions; private Transport mTransport = new Transport(); public ContentProvider() { } /** * Constructor just for mocking. * * @param context A Context object which should be some mock instance (like the * instance of {@link android.test.mock.MockContext}). * @param readPermission The read permision you want this instance should have in the * test, which is available via {@link #getReadPermission()}. * @param writePermission The write permission you want this instance should have * in the test, which is available via {@link #getWritePermission()}. * @param pathPermissions The PathPermissions you want this instance should have * in the test, which is available via {@link #getPathPermissions()}. * @hide */ public ContentProvider( Context context, String readPermission, String writePermission, PathPermission[] pathPermissions) { mContext = context; mReadPermission = readPermission; mWritePermission = writePermission; mPathPermissions = pathPermissions; } /** * Given an IContentProvider, try to coerce it back to the real * ContentProvider object if it is running in the local process. This can * be used if you know you are running in the same process as a provider, * and want to get direct access to its implementation details. Most * clients should not nor have a reason to use it. * * @param abstractInterface The ContentProvider interface that is to be * coerced. * @return If the IContentProvider is non-null and local, returns its actual * ContentProvider instance. Otherwise returns null. * @hide */ public static ContentProvider coerceToLocalContentProvider( IContentProvider abstractInterface) { if (abstractInterface instanceof Transport) { return ((Transport)abstractInterface).getContentProvider(); } return null; } /** * Binder object that deals with remoting. * * @hide */ class Transport extends ContentProviderNative { ContentProvider getContentProvider() { return ContentProvider.this; } /** * Remote version of a query, which returns an IBulkCursor. The bulk * cursor should be wrapped with BulkCursorToCursorAdaptor before use. */ public IBulkCursor bulkQuery(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, CursorWindow window) { enforceReadPermission(uri); Cursor cursor = ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder); if (cursor == null) { return null; } return new CursorToBulkCursorAdaptor(cursor, observer, ContentProvider.this.getClass().getName(), hasWritePermission(uri), window); } public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { enforceReadPermission(uri); return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder); } public String getType(Uri uri) { return ContentProvider.this.getType(uri); } public Uri insert(Uri uri, ContentValues initialValues) { enforceWritePermission(uri); return ContentProvider.this.insert(uri, initialValues); } public int bulkInsert(Uri uri, ContentValues[] initialValues) { enforceWritePermission(uri); return ContentProvider.this.bulkInsert(uri, initialValues); } public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { for (ContentProviderOperation operation : operations) { if (operation.isReadOperation()) { enforceReadPermission(operation.getUri()); } if (operation.isWriteOperation()) { enforceWritePermission(operation.getUri()); } } return ContentProvider.this.applyBatch(operations); } public int delete(Uri uri, String selection, String[] selectionArgs) { enforceWritePermission(uri); return ContentProvider.this.delete(uri, selection, selectionArgs); } public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { enforceWritePermission(uri); return ContentProvider.this.update(uri, values, selection, selectionArgs); } public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri); else enforceReadPermission(uri); return ContentProvider.this.openFile(uri, mode); } public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri); else enforceReadPermission(uri); return ContentProvider.this.openAssetFile(uri, mode); } /** * @hide */ public Bundle call(String method, String request, Bundle args) { return ContentProvider.this.call(method, request, args); } private void enforceReadPermission(Uri uri) { final int uid = Binder.getCallingUid(); if (uid == mMyUid) { return; } final Context context = getContext(); final String rperm = getReadPermission(); final int pid = Binder.getCallingPid(); if (rperm == null || context.checkPermission(rperm, pid, uid) == PackageManager.PERMISSION_GRANTED) { return; } PathPermission[] pps = getPathPermissions(); if (pps != null) { final String path = uri.getPath(); int i = pps.length; while (i > 0) { i--; final PathPermission pp = pps[i]; final String pprperm = pp.getReadPermission(); if (pprperm != null && pp.match(path)) { if (context.checkPermission(pprperm, pid, uid) == PackageManager.PERMISSION_GRANTED) { return; } } } } if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION) == PackageManager.PERMISSION_GRANTED) { return; } String msg = "Permission Denial: reading " + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + rperm; throw new SecurityException(msg); } private boolean hasWritePermission(Uri uri) { final int uid = Binder.getCallingUid(); if (uid == mMyUid) { return true; } final Context context = getContext(); final String wperm = getWritePermission(); final int pid = Binder.getCallingPid(); if (wperm == null || context.checkPermission(wperm, pid, uid) == PackageManager.PERMISSION_GRANTED) { return true; } PathPermission[] pps = getPathPermissions(); if (pps != null) { final String path = uri.getPath(); int i = pps.length; while (i > 0) { i--; final PathPermission pp = pps[i]; final String ppwperm = pp.getWritePermission(); if (ppwperm != null && pp.match(path)) { if (context.checkPermission(ppwperm, pid, uid) == PackageManager.PERMISSION_GRANTED) { return true; } } } } if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == PackageManager.PERMISSION_GRANTED) { return true; } return false; } private void enforceWritePermission(Uri uri) { if (hasWritePermission(uri)) { return; } String msg = "Permission Denial: writing " + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + getWritePermission(); throw new SecurityException(msg); } } /** * Retrieve the Context this provider is running in. Only available once * onCreate(Map icicle) has been called -- this will be null in the * constructor. */ public final Context getContext() { return mContext; } /** * Change the permission required to read data from the content * provider. This is normally set for you from its manifest information * when the provider is first created. * * @param permission Name of the permission required for read-only access. */ protected final void setReadPermission(String permission) { mReadPermission = permission; } /** * Return the name of the permission required for read-only access to * this content provider. This method can be called from multiple * threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. */ public final String getReadPermission() { return mReadPermission; } /** * Change the permission required to read and write data in the content * provider. This is normally set for you from its manifest information * when the provider is first created. * * @param permission Name of the permission required for read/write access. */ protected final void setWritePermission(String permission) { mWritePermission = permission; } /** * Return the name of the permission required for read/write access to * this content provider. This method can be called from multiple * threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. */ public final String getWritePermission() { return mWritePermission; } /** * Change the path-based permission required to read and/or write data in * the content provider. This is normally set for you from its manifest * information when the provider is first created. * * @param permissions Array of path permission descriptions. */ protected final void setPathPermissions(PathPermission[] permissions) { mPathPermissions = permissions; } /** * Return the path-based permissions required for read and/or write access to * this content provider. This method can be called from multiple * threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. */ public final PathPermission[] getPathPermissions() { return mPathPermissions; } /** * Called when the provider is being started. * * @return true if the provider was successfully loaded, false otherwise */ public abstract boolean onCreate(); public void onConfigurationChanged(Configuration newConfig) { } public void onLowMemory() { } /** * Receives a query request from a client in a local process, and * returns a Cursor. This is called internally by the {@link ContentResolver}. * This method can be called from multiple * threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * <p> * Example client call:<p> * <pre>// Request a specific record. * Cursor managedCursor = managedQuery( ContentUris.withAppendedId(Contacts.People.CONTENT_URI, 2), projection, // Which columns to return. null, // WHERE clause. null, // WHERE clause value substitution People.NAME + " ASC"); // Sort order.</pre> * Example implementation:<p> * <pre>// SQLiteQueryBuilder is a helper class that creates the // proper SQL syntax for us. SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder(); // Set the table we're querying. qBuilder.setTables(DATABASE_TABLE_NAME); // If the query ends in a specific record number, we're // being asked for a specific record, so set the // WHERE clause in our query. if((URI_MATCHER.match(uri)) == SPECIFIC_MESSAGE){ qBuilder.appendWhere("_id=" + uri.getPathLeafId()); } // Make the query. Cursor c = qBuilder.query(mDb, projection, selection, selectionArgs, groupBy, having, sortOrder); c.setNotificationUri(getContext().getContentResolver(), uri); return c;</pre> * * @param uri The URI to query. This will be the full URI sent by the client; * if the client is requesting a specific record, the URI will end in a record number * that the implementation should parse and add to a WHERE or HAVING clause, specifying * that _id value. * @param projection The list of columns to put into the cursor. If * null all columns are included. * @param selection A selection criteria to apply when filtering rows. * If null then all rows are included. * @param selectionArgs You may include ?s in selection, which will be replaced by * the values from selectionArgs, in order that they appear in the selection. * The values will be bound as Strings. * @param sortOrder How the rows in the cursor should be sorted. * If null then the provider is free to define the sort order. * @return a Cursor or null. */ public abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder); /** * Return the MIME type of the data at the given URI. This should start with * <code>vnd.android.cursor.item for a single record, * or <code>vnd.android.cursor.dir/ for multiple items. * This method can be called from multiple * threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * * @param uri the URI to query. * @return a MIME type string, or null if there is no type. */ public abstract String getType(Uri uri); /** * Implement this to insert a new row. * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after inserting. * This method can be called from multiple * threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * @param uri The content:// URI of the insertion request. * @param values A set of column_name/value pairs to add to the database. * @return The URI for the newly inserted item. */ public abstract Uri insert(Uri uri, ContentValues values); /** * Implement this to insert a set of new rows, or the default implementation will * iterate over the values and call {@link #insert} on each of them. * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after inserting. * This method can be called from multiple * threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * * @param uri The content:// URI of the insertion request. * @param values An array of sets of column_name/value pairs to add to the database. * @return The number of values that were inserted. */ public int bulkInsert(Uri uri, ContentValues[] values) { int numValues = values.length; for (int i = 0; i < numValues; i++) { insert(uri, values[i]); } return numValues; } /** * A request to delete one or more rows. The selection clause is applied when performing * the deletion, allowing the operation to affect multiple rows in a * directory. * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()} * after deleting. * This method can be called from multiple * threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * * <p>The implementation is responsible for parsing out a row ID at the end * of the URI, if a specific row is being deleted. That is, the client would * pass in <code>content://contacts/people/22 and the implementation is * responsible for parsing the record number (22) when creating a SQL statement. * * @param uri The full URI to query, including a row ID (if a specific record is requested). * @param selection An optional restriction to apply to rows when deleting. * @return The number of rows affected. * @throws SQLException */ public abstract int delete(Uri uri, String selection, String[] selectionArgs); /** * Update a content URI. All rows matching the optionally provided selection * will have their columns listed as the keys in the values map with the * values of those keys. * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after updating. * This method can be called from multiple * threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * * @param uri The URI to query. This can potentially have a record ID if this * is an update request for a specific record. * @param values A Bundle mapping from column names to new column values (NULL is a * valid value). * @param selection An optional filter to match rows to update. * @return the number of rows affected. */ public abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs); /** * Open a file blob associated with a content URI. * This method can be called from multiple * threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * * <p>Returns a * ParcelFileDescriptor, from which you can obtain a * {@link java.io.FileDescriptor} for use with * {@link java.io.FileInputStream}, {@link java.io.FileOutputStream}, etc. * This can be used to store large data (such as an image) associated with * a particular piece of content. * * <p>The returned ParcelFileDescriptor is owned by the caller, so it is * their responsibility to close it when done. That is, the implementation * of this method should create a new ParcelFileDescriptor for each call. * * @param uri The URI whose file is to be opened. * @param mode Access mode for the file. May be "r" for read-only access, * "rw" for read and write access, or "rwt" for read and write access * that truncates any existing file. * * @return Returns a new ParcelFileDescriptor which you can use to access * the file. * * @throws FileNotFoundException Throws FileNotFoundException if there is * no file associated with the given URI or the mode is invalid. * @throws SecurityException Throws SecurityException if the caller does * not have permission to access the file. * * @see #openAssetFile(Uri, String) * @see #openFileHelper(Uri, String) */ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { throw new FileNotFoundException("No files supported by provider at " + uri); } /** * This is like {@link #openFile}, but can be implemented by providers * that need to be able to return sub-sections of files, often assets * inside of their .apk. Note that when implementing this your clients * must be able to deal with such files, either directly with * {@link ContentResolver#openAssetFileDescriptor * ContentResolver.openAssetFileDescriptor}, or by using the higher-level * {@link ContentResolver#openInputStream ContentResolver.openInputStream} * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream} * methods. * * <p>Note: if you are implementing this to return a full file, you * should create the AssetFileDescriptor with * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with * applications that can not handle sub-sections of files.</em>

* * @param uri The URI whose file is to be opened. * @param mode Access mode for the file. May be "r" for read-only access, * "w" for write-only access (erasing whatever data is currently in * the file), "wa" for write-only access to append to any existing data, * "rw" for read and write access on any existing data, and "rwt" for read * and write access that truncates any existing file. * * @return Returns a new AssetFileDescriptor which you can use to access * the file. * * @throws FileNotFoundException Throws FileNotFoundException if there is * no file associated with the given URI or the mode is invalid. * @throws SecurityException Throws SecurityException if the caller does * not have permission to access the file. * * @see #openFile(Uri, String) * @see #openFileHelper(Uri, String) */ public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { ParcelFileDescriptor fd = openFile(uri, mode); return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; } /** * Convenience for subclasses that wish to implement {@link #openFile} * by looking up a column named "_data" at the given URI. * * @param uri The URI to be opened. * @param mode The file mode. May be "r" for read-only access, * "w" for write-only access (erasing whatever data is currently in * the file), "wa" for write-only access to append to any existing data, * "rw" for read and write access on any existing data, and "rwt" for read * and write access that truncates any existing file. * * @return Returns a new ParcelFileDescriptor that can be used by the * client to access the file. */ protected final ParcelFileDescriptor openFileHelper(Uri uri, String mode) throws FileNotFoundException { Cursor c = query(uri, new String[]{"_data"}, null, null, null); int count = (c != null) ? c.getCount() : 0; if (count != 1) { // If there is not exactly one result, throw an appropriate // exception. if (c != null) { c.close(); } if (count == 0) { throw new FileNotFoundException("No entry for " + uri); } throw new FileNotFoundException("Multiple items at " + uri); } c.moveToFirst(); int i = c.getColumnIndex("_data"); String path = (i >= 0 ? c.getString(i) : null); c.close(); if (path == null) { throw new FileNotFoundException("Column _data not found."); } int modeBits = ContentResolver.modeToMode(uri, mode); return ParcelFileDescriptor.open(new File(path), modeBits); } /** * Returns true if this instance is a temporary content provider. * @return true if this instance is a temporary content provider */ protected boolean isTemporary() { return false; } /** * Returns the Binder object for this provider. * * @return the Binder object for this provider * @hide */ public IContentProvider getIContentProvider() { return mTransport; } /** * After being instantiated, this is called to tell the content provider * about itself. * * @param context The context this provider is running in * @param info Registered information about this content provider */ public void attachInfo(Context context, ProviderInfo info) { /* * Only allow it to be set once, so after the content service gives * this to us clients can't change it. */ if (mContext == null) { mContext = context; mMyUid = Process.myUid(); if (info != null) { setReadPermission(info.readPermission); setWritePermission(info.writePermission); setPathPermissions(info.pathPermissions); } ContentProvider.this.onCreate(); } } /** * Applies each of the {@link ContentProviderOperation} objects and returns an array * of their results. Passes through OperationApplicationException, which may be thrown * by the call to {@link ContentProviderOperation#apply}. * If all the applications succeed then a {@link ContentProviderResult} array with the * same number of elements as the operations will be returned. It is implementation-specific * how many, if any, operations will have been successfully applied if a call to * apply results in a {@link OperationApplicationException}. * @param operations the operations to apply * @return the results of the applications * @throws OperationApplicationException thrown if an application fails. * See {@link ContentProviderOperation#apply} for more information. */ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { final int numOperations = operations.size(); final ContentProviderResult[] results = new ContentProviderResult[numOperations]; for (int i = 0; i < numOperations; i++) { results[i] = operations.get(i).apply(this, results, i); } return results; } /** * @hide -- until interface has proven itself * * Call an provider-defined method. This can be used to implement * interfaces that are cheaper than using a Cursor. * * @param method Method name to call. Opaque to framework. * @param request Nullable String argument passed to method. * @param args Nullable Bundle argument passed to method. */ public Bundle call(String method, String request, Bundle args) { return null; } }
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.