/core/java/android/content/ContentResolver.java
Java | 1111 lines | 437 code | 72 blank | 602 comment | 79 complexity | 4319293a31429d76d3bdac310a0b6d25 MD5 | raw file
- /*
- * 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.accounts.Account;
- import android.app.ActivityManagerNative;
- import android.app.ActivityThread;
- import android.app.AppGlobals;
- import android.content.pm.PackageManager.NameNotFoundException;
- import android.content.res.AssetFileDescriptor;
- import android.content.res.Resources;
- import android.database.ContentObserver;
- import android.database.CrossProcessCursorWrapper;
- import android.database.Cursor;
- import android.database.IContentObserver;
- import android.graphics.Point;
- import android.net.Uri;
- import android.os.Bundle;
- import android.os.CancellationSignal;
- import android.os.DeadObjectException;
- import android.os.IBinder;
- import android.os.ICancellationSignal;
- import android.os.OperationCanceledException;
- import android.os.ParcelFileDescriptor;
- import android.os.RemoteException;
- import android.os.ServiceManager;
- import android.os.SystemClock;
- import android.os.UserHandle;
- import android.text.TextUtils;
- import android.util.EventLog;
- import android.util.Log;
- import dalvik.system.CloseGuard;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Random;
- /**
- * This class provides applications access to the content model.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using a ContentResolver with content providers, read the
- * <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
- * developer guide.</p>
- */
- public abstract class ContentResolver {
- /**
- * @deprecated instead use
- * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
- */
- @Deprecated
- public static final String SYNC_EXTRAS_ACCOUNT = "account";
- /**
- * If this extra is set to true, the sync request will be scheduled
- * at the front of the sync request queue and without any delay
- */
- public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
- /**
- * @deprecated instead use
- * {@link #SYNC_EXTRAS_MANUAL}
- */
- @Deprecated
- public static final String SYNC_EXTRAS_FORCE = "force";
- /**
- * If this extra is set to true then the sync settings (like getSyncAutomatically())
- * are ignored by the sync scheduler.
- */
- public static final String SYNC_EXTRAS_IGNORE_SETTINGS = "ignore_settings";
- /**
- * If this extra is set to true then any backoffs for the initial attempt (e.g. due to retries)
- * are ignored by the sync scheduler. If this request fails and gets rescheduled then the
- * retries will still honor the backoff.
- */
- public static final String SYNC_EXTRAS_IGNORE_BACKOFF = "ignore_backoff";
- /**
- * If this extra is set to true then the request will not be retried if it fails.
- */
- public static final String SYNC_EXTRAS_DO_NOT_RETRY = "do_not_retry";
- /**
- * Setting this extra is the equivalent of setting both {@link #SYNC_EXTRAS_IGNORE_SETTINGS}
- * and {@link #SYNC_EXTRAS_IGNORE_BACKOFF}
- */
- public static final String SYNC_EXTRAS_MANUAL = "force";
- /**
- * Indicates that this sync is intended to only upload local changes to the server.
- * For example, this will be set to true if the sync is initiated by a call to
- * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
- */
- public static final String SYNC_EXTRAS_UPLOAD = "upload";
- /**
- * Indicates that the sync adapter should proceed with the delete operations,
- * even if it determines that there are too many.
- * See {@link SyncResult#tooManyDeletions}
- */
- public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
- /**
- * Indicates that the sync adapter should not proceed with the delete operations,
- * if it determines that there are too many.
- * See {@link SyncResult#tooManyDeletions}
- */
- public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
- /* Extensions to API. TODO: Not clear if we will keep these as public flags. */
- /** {@hide} User-specified flag for expected upload size. */
- public static final String SYNC_EXTRAS_EXPECTED_UPLOAD = "expected_upload";
- /** {@hide} User-specified flag for expected download size. */
- public static final String SYNC_EXTRAS_EXPECTED_DOWNLOAD = "expected_download";
- /** {@hide} Priority of this sync with respect to other syncs scheduled for this application. */
- public static final String SYNC_EXTRAS_PRIORITY = "sync_priority";
- /** {@hide} Flag to allow sync to occur on metered network. */
- public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered";
- /**
- * Set by the SyncManager to request that the SyncAdapter initialize itself for
- * the given account/authority pair. One required initialization step is to
- * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
- * called with a >= 0 value. When this flag is set the SyncAdapter does not need to
- * do a full sync, though it is allowed to do so.
- */
- public static final String SYNC_EXTRAS_INITIALIZE = "initialize";
- /** @hide */
- public static final Intent ACTION_SYNC_CONN_STATUS_CHANGED =
- new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
- public static final String SCHEME_CONTENT = "content";
- public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
- public static final String SCHEME_FILE = "file";
- /**
- * An extra {@link Point} describing the optimal size for a requested image
- * resource, in pixels. If a provider has multiple sizes of the image, it
- * should return the image closest to this size.
- *
- * @see #openTypedAssetFileDescriptor(Uri, String, Bundle)
- * @see #openTypedAssetFileDescriptor(Uri, String, Bundle,
- * CancellationSignal)
- */
- public static final String EXTRA_SIZE = "android.content.extra.SIZE";
- /**
- * This is the Android platform's base MIME type for a content: URI
- * containing a Cursor of a single item. Applications should use this
- * as the base type along with their own sub-type of their content: URIs
- * that represent a particular item. For example, hypothetical IMAP email
- * client may have a URI
- * <code>content://com.company.provider.imap/inbox/1</code> for a particular
- * message in the inbox, whose MIME type would be reported as
- * <code>CURSOR_ITEM_BASE_TYPE + "/vnd.company.imap-msg"</code>
- *
- * <p>Compare with {@link #CURSOR_DIR_BASE_TYPE}.
- */
- public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
- /**
- * This is the Android platform's base MIME type for a content: URI
- * containing a Cursor of zero or more items. Applications should use this
- * as the base type along with their own sub-type of their content: URIs
- * that represent a directory of items. For example, hypothetical IMAP email
- * client may have a URI
- * <code>content://com.company.provider.imap/inbox</code> for all of the
- * messages in its inbox, whose MIME type would be reported as
- * <code>CURSOR_DIR_BASE_TYPE + "/vnd.company.imap-msg"</code>
- *
- * <p>Note how the base MIME type varies between this and
- * {@link #CURSOR_ITEM_BASE_TYPE} depending on whether there is
- * one single item or multiple items in the data set, while the sub-type
- * remains the same because in either case the data structure contained
- * in the cursor is the same.
- */
- public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
- /**
- * This is the Android platform's generic MIME type to match any MIME
- * type of the form "{@link #CURSOR_ITEM_BASE_TYPE}/{@code SUB_TYPE}".
- * {@code SUB_TYPE} is the sub-type of the application-dependent
- * content, e.g., "audio", "video", "playlist".
- */
- public static final String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
- /** @hide */
- public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
- /** @hide */
- public static final int SYNC_ERROR_AUTHENTICATION = 2;
- /** @hide */
- public static final int SYNC_ERROR_IO = 3;
- /** @hide */
- public static final int SYNC_ERROR_PARSE = 4;
- /** @hide */
- public static final int SYNC_ERROR_CONFLICT = 5;
- /** @hide */
- public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6;
- /** @hide */
- public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7;
- /** @hide */
- public static final int SYNC_ERROR_INTERNAL = 8;
- private static final String[] SYNC_ERROR_NAMES = new String[] {
- "already-in-progress",
- "authentication-error",
- "io-error",
- "parse-error",
- "conflict",
- "too-many-deletions",
- "too-many-retries",
- "internal-error",
- };
- /** @hide */
- public static String syncErrorToString(int error) {
- if (error < 1 || error > SYNC_ERROR_NAMES.length) {
- return String.valueOf(error);
- }
- return SYNC_ERROR_NAMES[error - 1];
- }
- /** @hide */
- public static int syncErrorStringToInt(String error) {
- for (int i = 0, n = SYNC_ERROR_NAMES.length; i < n; i++) {
- if (SYNC_ERROR_NAMES[i].equals(error)) {
- return i + 1;
- }
- }
- if (error != null) {
- try {
- return Integer.parseInt(error);
- } catch (NumberFormatException e) {
- Log.d(TAG, "error parsing sync error: " + error);
- }
- }
- return 0;
- }
- public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0;
- public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1;
- public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2;
- /** @hide */
- public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3;
- /** @hide */
- public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
- // Always log queries which take 500ms+; shorter queries are
- // sampled accordingly.
- private static final boolean ENABLE_CONTENT_SAMPLE = false;
- private static final int SLOW_THRESHOLD_MILLIS = 500;
- private final Random mRandom = new Random(); // guarded by itself
- public ContentResolver(Context context) {
- mContext = context != null ? context : ActivityThread.currentApplication();
- mPackageName = mContext.getOpPackageName();
- }
- /** @hide */
- protected abstract IContentProvider acquireProvider(Context c, String name);
- /**
- * Providing a default implementation of this, to avoid having to change a
- * lot of other things, but implementations of ContentResolver should
- * implement it.
- *
- * @hide
- */
- protected IContentProvider acquireExistingProvider(Context c, String name) {
- return acquireProvider(c, name);
- }
- /** @hide */
- public abstract boolean releaseProvider(IContentProvider icp);
- /** @hide */
- protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
- /** @hide */
- public abstract boolean releaseUnstableProvider(IContentProvider icp);
- /** @hide */
- public abstract void unstableProviderDied(IContentProvider icp);
- /** @hide */
- public void appNotRespondingViaProvider(IContentProvider icp) {
- throw new UnsupportedOperationException("appNotRespondingViaProvider");
- }
- /**
- * Return the MIME type of the given content URL.
- *
- * @param url A Uri identifying content (either a list or specific type),
- * using the content:// scheme.
- * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
- */
- public final String getType(Uri url) {
- // XXX would like to have an acquireExistingUnstableProvider for this.
- IContentProvider provider = acquireExistingProvider(url);
- if (provider != null) {
- try {
- return provider.getType(url);
- } catch (RemoteException e) {
- return null;
- } catch (java.lang.Exception e) {
- Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
- return null;
- } finally {
- releaseProvider(provider);
- }
- }
- if (!SCHEME_CONTENT.equals(url.getScheme())) {
- return null;
- }
- try {
- String type = ActivityManagerNative.getDefault().getProviderMimeType(
- ContentProvider.getUriWithoutUserId(url), resolveUserId(url));
- return type;
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } catch (java.lang.Exception e) {
- Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
- return null;
- }
- }
- /**
- * Query for the possible MIME types for the representations the given
- * content URL can be returned when opened as as stream with
- * {@link #openTypedAssetFileDescriptor}. Note that the types here are
- * not necessarily a superset of the type returned by {@link #getType} --
- * many content providers cannot return a raw stream for the structured
- * data that they contain.
- *
- * @param url A Uri identifying content (either a list or specific type),
- * using the content:// scheme.
- * @param mimeTypeFilter The desired MIME type. This may be a pattern,
- * such as */*, to query for all available MIME types that match the
- * pattern.
- * @return Returns an array of MIME type strings for all available
- * data streams that match the given mimeTypeFilter. If there are none,
- * null is returned.
- */
- public String[] getStreamTypes(Uri url, String mimeTypeFilter) {
- IContentProvider provider = acquireProvider(url);
- if (provider == null) {
- return null;
- }
- try {
- return provider.getStreamTypes(url, mimeTypeFilter);
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } finally {
- releaseProvider(provider);
- }
- }
- /**
- * Query the given URI, returning a {@link Cursor} over the result set.
- * <p>
- * For best performance, the caller should follow these guidelines:
- * <ul>
- * <li>Provide an explicit projection, to prevent
- * reading data from storage that aren't going to be used.</li>
- * <li>Use question mark parameter markers such as 'phone=?' instead of
- * explicit values in the {@code selection} parameter, so that queries
- * that differ only by those values will be recognized as the same
- * for caching purposes.</li>
- * </ul>
- * </p>
- *
- * @param uri The URI, using the content:// scheme, for the content to
- * retrieve.
- * @param projection A list of which columns to return. Passing null will
- * return all columns, which is inefficient.
- * @param selection A filter declaring which rows to return, formatted as an
- * SQL WHERE clause (excluding the WHERE itself). Passing null will
- * return all rows for the given URI.
- * @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in the order that they
- * appear in the selection. The values will be bound as Strings.
- * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
- * clause (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @return A Cursor object, which is positioned before the first entry, or null
- * @see Cursor
- */
- public final Cursor query(Uri uri, String[] projection,
- String selection, String[] selectionArgs, String sortOrder) {
- return query(uri, projection, selection, selectionArgs, sortOrder, null);
- }
- /**
- * Query the given URI, returning a {@link Cursor} over the result set
- * with optional support for cancellation.
- * <p>
- * For best performance, the caller should follow these guidelines:
- * <ul>
- * <li>Provide an explicit projection, to prevent
- * reading data from storage that aren't going to be used.</li>
- * <li>Use question mark parameter markers such as 'phone=?' instead of
- * explicit values in the {@code selection} parameter, so that queries
- * that differ only by those values will be recognized as the same
- * for caching purposes.</li>
- * </ul>
- * </p>
- *
- * @param uri The URI, using the content:// scheme, for the content to
- * retrieve.
- * @param projection A list of which columns to return. Passing null will
- * return all columns, which is inefficient.
- * @param selection A filter declaring which rows to return, formatted as an
- * SQL WHERE clause (excluding the WHERE itself). Passing null will
- * return all rows for the given URI.
- * @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in the order that they
- * appear in the selection. The values will be bound as Strings.
- * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
- * clause (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return A Cursor object, which is positioned before the first entry, or null
- * @see Cursor
- */
- public final Cursor query(final Uri uri, String[] projection,
- String selection, String[] selectionArgs, String sortOrder,
- CancellationSignal cancellationSignal) {
- IContentProvider unstableProvider = acquireUnstableProvider(uri);
- if (unstableProvider == null) {
- return null;
- }
- IContentProvider stableProvider = null;
- Cursor qCursor = null;
- try {
- long startTime = SystemClock.uptimeMillis();
- ICancellationSignal remoteCancellationSignal = null;
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
- remoteCancellationSignal = unstableProvider.createCancellationSignal();
- cancellationSignal.setRemote(remoteCancellationSignal);
- }
- try {
- qCursor = unstableProvider.query(mPackageName, uri, projection,
- selection, selectionArgs, sortOrder, remoteCancellationSignal);
- } catch (DeadObjectException e) {
- // The remote process has died... but we only hold an unstable
- // reference though, so we might recover!!! Let's try!!!!
- // This is exciting!!1!!1!!!!1
- unstableProviderDied(unstableProvider);
- stableProvider = acquireProvider(uri);
- if (stableProvider == null) {
- return null;
- }
- qCursor = stableProvider.query(mPackageName, uri, projection,
- selection, selectionArgs, sortOrder, remoteCancellationSignal);
- }
- if (qCursor == null) {
- return null;
- }
- // Force query execution. Might fail and throw a runtime exception here.
- qCursor.getCount();
- long durationMillis = SystemClock.uptimeMillis() - startTime;
- maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
- // Wrap the cursor object into CursorWrapperInner object.
- CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
- stableProvider != null ? stableProvider : acquireProvider(uri));
- stableProvider = null;
- qCursor = null;
- return wrapper;
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } finally {
- if (qCursor != null) {
- qCursor.close();
- }
- if (cancellationSignal != null) {
- cancellationSignal.setRemote(null);
- }
- if (unstableProvider != null) {
- releaseUnstableProvider(unstableProvider);
- }
- if (stableProvider != null) {
- releaseProvider(stableProvider);
- }
- }
- }
- /**
- * Transform the given <var>url</var> to a canonical representation of
- * its referenced resource, which can be used across devices, persisted,
- * backed up and restored, etc. The returned Uri is still a fully capable
- * Uri for use with its content provider, allowing you to do all of the
- * same content provider operations as with the original Uri --
- * {@link #query}, {@link #openInputStream(android.net.Uri)}, etc. The
- * only difference in behavior between the original and new Uris is that
- * the content provider may need to do some additional work at each call
- * using it to resolve it to the correct resource, especially if the
- * canonical Uri has been moved to a different environment.
- *
- * <p>If you are moving a canonical Uri between environments, you should
- * perform another call to {@link #canonicalize} with that original Uri to
- * re-canonicalize it for the current environment. Alternatively, you may
- * want to use {@link #uncanonicalize} to transform it to a non-canonical
- * Uri that works only in the current environment but potentially more
- * efficiently than the canonical representation.</p>
- *
- * @param url The {@link Uri} that is to be transformed to a canonical
- * representation. Like all resolver calls, the input can be either
- * a non-canonical or canonical Uri.
- *
- * @return Returns the official canonical representation of <var>url</var>,
- * or null if the content provider does not support a canonical representation
- * of the given Uri. Many providers may not support canonicalization of some
- * or all of their Uris.
- *
- * @see #uncanonicalize
- */
- public final Uri canonicalize(Uri url) {
- IContentProvider provider = acquireProvider(url);
- if (provider == null) {
- return null;
- }
- try {
- return provider.canonicalize(mPackageName, url);
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } finally {
- releaseProvider(provider);
- }
- }
- /**
- * Given a canonical Uri previously generated by {@link #canonicalize}, convert
- * it to its local non-canonical form. This can be useful in some cases where
- * you know that you will only be using the Uri in the current environment and
- * want to avoid any possible overhead when using it with the content
- * provider or want to verify that the referenced data exists at all in the
- * new environment.
- *
- * @param url The canonical {@link Uri} that is to be convered back to its
- * non-canonical form.
- *
- * @return Returns the non-canonical representation of <var>url</var>. This will
- * return null if data identified by the canonical Uri can not be found in
- * the current environment; callers must always check for null and deal with
- * that by appropriately falling back to an alternative.
- *
- * @see #canonicalize
- */
- public final Uri uncanonicalize(Uri url) {
- IContentProvider provider = acquireProvider(url);
- if (provider == null) {
- return null;
- }
- try {
- return provider.uncanonicalize(mPackageName, url);
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } finally {
- releaseProvider(provider);
- }
- }
- /**
- * Open a stream on to the content associated with a content URI. If there
- * is no data associated with the URI, FileNotFoundException is thrown.
- *
- * <h5>Accepts the following URI schemes:</h5>
- * <ul>
- * <li>content ({@link #SCHEME_CONTENT})</li>
- * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
- * <li>file ({@link #SCHEME_FILE})</li>
- * </ul>
- *
- * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
- * on these schemes.
- *
- * @param uri The desired URI.
- * @return InputStream
- * @throws FileNotFoundException if the provided URI could not be opened.
- * @see #openAssetFileDescriptor(Uri, String)
- */
- public final InputStream openInputStream(Uri uri)
- throws FileNotFoundException {
- String scheme = uri.getScheme();
- if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
- // Note: left here to avoid breaking compatibility. May be removed
- // with sufficient testing.
- OpenResourceIdResult r = getResourceId(uri);
- try {
- InputStream stream = r.r.openRawResource(r.id);
- return stream;
- } catch (Resources.NotFoundException ex) {
- throw new FileNotFoundException("Resource does not exist: " + uri);
- }
- } else if (SCHEME_FILE.equals(scheme)) {
- // Note: left here to avoid breaking compatibility. May be removed
- // with sufficient testing.
- return new FileInputStream(uri.getPath());
- } else {
- AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r", null);
- try {
- return fd != null ? fd.createInputStream() : null;
- } catch (IOException e) {
- throw new FileNotFoundException("Unable to create stream");
- }
- }
- }
- /**
- * Synonym for {@link #openOutputStream(Uri, String)
- * openOutputStream(uri, "w")}.
- * @throws FileNotFoundException if the provided URI could not be opened.
- */
- public final OutputStream openOutputStream(Uri uri)
- throws FileNotFoundException {
- return openOutputStream(uri, "w");
- }
- /**
- * Open a stream on to the content associated with a content URI. If there
- * is no data associated with the URI, FileNotFoundException is thrown.
- *
- * <h5>Accepts the following URI schemes:</h5>
- * <ul>
- * <li>content ({@link #SCHEME_CONTENT})</li>
- * <li>file ({@link #SCHEME_FILE})</li>
- * </ul>
- *
- * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
- * on these schemes.
- *
- * @param uri The desired URI.
- * @param mode May be "w", "wa", "rw", or "rwt".
- * @return OutputStream
- * @throws FileNotFoundException if the provided URI could not be opened.
- * @see #openAssetFileDescriptor(Uri, String)
- */
- public final OutputStream openOutputStream(Uri uri, String mode)
- throws FileNotFoundException {
- AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode, null);
- try {
- return fd != null ? fd.createOutputStream() : null;
- } catch (IOException e) {
- throw new FileNotFoundException("Unable to create stream");
- }
- }
- /**
- * Open a raw file descriptor to access data under a URI. This
- * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
- * underlying {@link ContentProvider#openFile}
- * ContentProvider.openFile()} method, so will <em>not</em> work with
- * providers that return sub-sections of files. If at all possible,
- * you should use {@link #openAssetFileDescriptor(Uri, String)}. You
- * will receive a FileNotFoundException exception if the provider returns a
- * sub-section of a file.
- *
- * <h5>Accepts the following URI schemes:</h5>
- * <ul>
- * <li>content ({@link #SCHEME_CONTENT})</li>
- * <li>file ({@link #SCHEME_FILE})</li>
- * </ul>
- *
- * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
- * on these schemes.
- * <p>
- * If opening with the exclusive "r" or "w" modes, the returned
- * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
- * of data. Opening with the "rw" mode implies a file on disk that supports
- * seeking. If possible, always use an exclusive mode to give the underlying
- * {@link ContentProvider} the most flexibility.
- * <p>
- * If you are writing a file, and need to communicate an error to the
- * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
- *
- * @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openFile
- * ContentProvider.openFile}.
- * @return Returns a new ParcelFileDescriptor pointing to the file. You
- * own this descriptor and are responsible for closing it when done.
- * @throws FileNotFoundException Throws FileNotFoundException if no
- * file exists under the URI or the mode is invalid.
- * @see #openAssetFileDescriptor(Uri, String)
- */
- public final ParcelFileDescriptor openFileDescriptor(Uri uri, String mode)
- throws FileNotFoundException {
- return openFileDescriptor(uri, mode, null);
- }
- /**
- * Open a raw file descriptor to access data under a URI. This
- * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
- * underlying {@link ContentProvider#openFile}
- * ContentProvider.openFile()} method, so will <em>not</em> work with
- * providers that return sub-sections of files. If at all possible,
- * you should use {@link #openAssetFileDescriptor(Uri, String)}. You
- * will receive a FileNotFoundException exception if the provider returns a
- * sub-section of a file.
- *
- * <h5>Accepts the following URI schemes:</h5>
- * <ul>
- * <li>content ({@link #SCHEME_CONTENT})</li>
- * <li>file ({@link #SCHEME_FILE})</li>
- * </ul>
- *
- * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
- * on these schemes.
- * <p>
- * If opening with the exclusive "r" or "w" modes, the returned
- * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
- * of data. Opening with the "rw" mode implies a file on disk that supports
- * seeking. If possible, always use an exclusive mode to give the underlying
- * {@link ContentProvider} the most flexibility.
- * <p>
- * If you are writing a file, and need to communicate an error to the
- * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
- *
- * @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openFile
- * ContentProvider.openFile}.
- * @param cancellationSignal A signal to cancel the operation in progress,
- * or null if none. If the operation is canceled, then
- * {@link OperationCanceledException} will be thrown.
- * @return Returns a new ParcelFileDescriptor pointing to the file. You
- * own this descriptor and are responsible for closing it when done.
- * @throws FileNotFoundException Throws FileNotFoundException if no
- * file exists under the URI or the mode is invalid.
- * @see #openAssetFileDescriptor(Uri, String)
- */
- public final ParcelFileDescriptor openFileDescriptor(Uri uri,
- String mode, CancellationSignal cancellationSignal) throws FileNotFoundException {
- AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode, cancellationSignal);
- if (afd == null) {
- return null;
- }
- if (afd.getDeclaredLength() < 0) {
- // This is a full file!
- return afd.getParcelFileDescriptor();
- }
- // Client can't handle a sub-section of a file, so close what
- // we got and bail with an exception.
- try {
- afd.close();
- } catch (IOException e) {
- }
- throw new FileNotFoundException("Not a whole file");
- }
- /**
- * Open a raw file descriptor to access data under a URI. This
- * interacts with the underlying {@link ContentProvider#openAssetFile}
- * method of the provider associated with the given URI, to retrieve any file stored there.
- *
- * <h5>Accepts the following URI schemes:</h5>
- * <ul>
- * <li>content ({@link #SCHEME_CONTENT})</li>
- * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
- * <li>file ({@link #SCHEME_FILE})</li>
- * </ul>
- * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
- * <p>
- * A Uri object can be used to reference a resource in an APK file. The
- * Uri should be one of the following formats:
- * <ul>
- * <li><code>android.resource://package_name/id_number</code><br/>
- * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
- * For example <code>com.example.myapp</code><br/>
- * <code>id_number</code> is the int form of the ID.<br/>
- * The easiest way to construct this form is
- * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
- * </li>
- * <li><code>android.resource://package_name/type/name</code><br/>
- * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
- * For example <code>com.example.myapp</code><br/>
- * <code>type</code> is the string form of the resource type. For example, <code>raw</code>
- * or <code>drawable</code>.
- * <code>name</code> is the string form of the resource name. That is, whatever the file
- * name was in your res directory, without the type extension.
- * The easiest way to construct this form is
- * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
- * </li>
- * </ul>
- *
- * <p>Note that if this function is called for read-only input (mode is "r")
- * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
- * for you with a MIME type of "*/*". This allows such callers to benefit
- * from any built-in data conversion that a provider implements.
- *
- * @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
- * ContentProvider.openAssetFile}.
- * @return Returns a new ParcelFileDescriptor pointing to the file. You
- * own this descriptor and are responsible for closing it when done.
- * @throws FileNotFoundException Throws FileNotFoundException of no
- * file exists under the URI or the mode is invalid.
- */
- public final AssetFileDescriptor openAssetFileDescriptor(Uri uri, String mode)
- throws FileNotFoundException {
- return openAssetFileDescriptor(uri, mode, null);
- }
- /**
- * Open a raw file descriptor to access data under a URI. This
- * interacts with the underlying {@link ContentProvider#openAssetFile}
- * method of the provider associated with the given URI, to retrieve any file stored there.
- *
- * <h5>Accepts the following URI schemes:</h5>
- * <ul>
- * <li>content ({@link #SCHEME_CONTENT})</li>
- * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
- * <li>file ({@link #SCHEME_FILE})</li>
- * </ul>
- * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
- * <p>
- * A Uri object can be used to reference a resource in an APK file. The
- * Uri should be one of the following formats:
- * <ul>
- * <li><code>android.resource://package_name/id_number</code><br/>
- * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
- * For example <code>com.example.myapp</code><br/>
- * <code>id_number</code> is the int form of the ID.<br/>
- * The easiest way to construct this form is
- * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
- * </li>
- * <li><code>android.resource://package_name/type/name</code><br/>
- * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
- * For example <code>com.example.myapp</code><br/>
- * <code>type</code> is the string form of the resource type. For example, <code>raw</code>
- * or <code>drawable</code>.
- * <code>name</code> is the string form of the resource name. That is, whatever the file
- * name was in your res directory, without the type extension.
- * The easiest way to construct this form is
- * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
- * </li>
- * </ul>
- *
- * <p>Note that if this function is called for read-only input (mode is "r")
- * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
- * for you with a MIME type of "*/*". This allows such callers to benefit
- * from any built-in data conversion that a provider implements.
- *
- * @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
- * ContentProvider.openAssetFile}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if
- * none. If the operation is canceled, then
- * {@link OperationCanceledException} will be thrown.
- * @return Returns a new ParcelFileDescriptor pointing to the file. You
- * own this descriptor and are responsible for closing it when done.
- * @throws FileNotFoundException Throws FileNotFoundException of no
- * file exists under the URI or the mode is invalid.
- */
- public final AssetFileDescriptor openAssetFileDescriptor(Uri uri,
- String mode, CancellationSignal cancellationSignal) throws FileNotFoundException {
- String scheme = uri.getScheme();
- if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
- if (!"r".equals(mode)) {
- throw new FileNotFoundException("Can't write resources: " + uri);
- }
- OpenResourceIdResult r = getResourceId(uri);
- try {
- return r.r.openRawResourceFd(r.id);
- } catch (Resources.NotFoundException ex) {
- throw new FileNotFoundException("Resource does not exist: " + uri);
- }
- } else if (SCHEME_FILE.equals(scheme)) {
- ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
- new File(uri.getPath()), ParcelFileDescriptor.parseMode(mode));
- return new AssetFileDescriptor(pfd, 0, -1);
- } else {
- if ("r".equals(mode)) {
- return openTypedAssetFileDescriptor(uri, "*/*", null, cancellationSignal);
- } else {
- IContentProvider unstableProvider = acquireUnstableProvider(uri);
- if (unstableProvider == null) {
- throw new FileNotFoundException("No content provider: " + uri);
- }
- IContentProvider stableProvider = null;
- AssetFileDescriptor fd = null;
- try {
- ICancellationSignal remoteCancellationSignal = null;
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
- remoteCancellationSignal = unstableProvider.createCancellationSignal();
- cancellationSignal.setRemote(remoteCancellationSignal);
- }
- try {
- fd = unstableProvider.openAssetFile(
- mPackageName, uri, mode, remoteCancellationSignal);
- if (fd == null) {
- // The provider will be released by the finally{} clause
- return null;
- }
- } catch (DeadObjectException e) {
- // The remote process has died... but we only hold an unstable
- // reference though, so we might recover!!! Let's try!!!!
- // This is exciting!!1!!1!!!!1
- unstableProviderDied(unstableProvider);
- stableProvider = acquireProvider(uri);
- if (stableProvider == null) {
- throw new FileNotFoundException("No content provider: " + uri);
- }
- fd = stableProvider.openAssetFile(
- mPackageName, uri, mode, remoteCancellationSignal);
- if (fd == null) {
- // The provider will be released by the finally{} clause
- return null;
- }
- }
- if (stableProvider == null) {
- stableProvider = acquireProvider(uri);
- }
- releaseUnstableProvider(unstableProvider);
- ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
- fd.getParcelFileDescriptor(), stableProvider);
- // Success! Don't release the provider when exiting, let
- // ParcelFileDescriptorInner do that when it is closed.
- stableProvider = null;
- return new AssetFileDescriptor(pfd, fd.getStartOffset(),
- fd.getDeclaredLength());
- } catch (RemoteException e) {
- // Whatever, whatever, we'll go away.
- throw new FileNotFoundException(
- "Failed opening content provider: " + uri);
- } catch (FileNotFoundException e) {
- throw e;
- } finally {
- if (cancellationSignal != null) {
- cancellationSignal.setRemote(null);
- }
- if (stableProvider != null) {
- releaseProvider(stableProvider);
- }
- if (unstableProvider != null) {
- releaseUnstableProvider(unstableProvider);
- }
- }
- }
- }
- }
- /**
- * Open a raw file descriptor to access (potentially type transformed)
- * data from a "content:" URI. This interacts with the underlying
- * {@link ContentProvider#openTypedAssetFile} method of the provider
- * associated with the given URI, to retrieve retrieve any appropriate
- * data stream for the data stored there.
- *
- * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
- * with "content:" URIs, because content providers are the only facility
- * with an associated MIME type to ensure that the returned data stream
- * is of the desired type.
- *
- * <p>All text/* streams are encoded in UTF-8.
- *
- * @param uri The desired URI to open.
- * @param mimeType The desired MIME type of the returned data. This can
- * be a pattern such as */*, which will allow the content provider to
- * select a type, though there is no way for you to determine what type
- * it is returning.
- * @param opts Additional provider-dependent options.
- * @return Returns a new ParcelFileDescriptor from which you can read the
- * data stream from the provider. Note that this may be a pipe, meaning
- * you can't seek in it. The only seek you should do is if the
- * AssetFileDescriptor contains an offset, to move to that offset before
- * reading. You own this descriptor and are responsible for closing it when done.
- * @throws FileNotFoundException Throws FileNotFoundException of no
- * data of the desired type exists under the URI.
- */
- public final AssetFileDescriptor openTypedAssetFileDescriptor(
- Uri uri, String mimeType, Bundle opts) throws FileNotFoundException {
- return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
- }
- /**
- * Open a raw file descriptor to access (potentially type transformed)
- * data from a "content:" URI. This interacts with the underlying
- * {@link ContentProvider#openTypedAssetFile} method of the provider
- * associated with the given URI, to retrieve retrieve any appropriate
- * data stream for the data stored there.
- *
- * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
- * with "content:" URIs, because content providers are the only facility
- * with an associated MIME type to ensure that the returned data stream
- * is of the desired type.
- *
- * <p>All text/* streams are encoded in UTF-8.
- *
- * @param uri The desired URI to open.
- * @param mimeType The desired MIME type of the returned data. This can
- * be a pattern such as */*, which will allow the content provider to
- * select a type, though there is no way for you to determine what type
- * it is returning.
- * @param opts Additional provider-dependent options.
- * @param cancellationSignal A signal to cancel the operation in progress,
- * or null if none. If the operation is canceled, then
- * {@link OperationCanceledException} will be thrown.
- * @return Returns a new ParcelFileDescriptor from which you can read the
- * data stream from the provider. Note that this may be a pipe, meaning
- * you can't seek in it. The only seek you should do is if the
- * AssetFileDescriptor contains an offset, to move to that offset before
- * reading. You own this descriptor and are responsible for closing it when done.
- * @throws FileNotFoundException Throws FileNotFoundException of no
- * data of the desired type exists under the URI.
- */
- public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
- String mimeType, Bundle opts, CancellationSignal cancellationSignal)
- throws FileNotFoundException {
- IContentProvider unstableProvider = acquireUnstableProvider(uri);
- if (unstableProvider == null) {
- throw new FileNotFoundException("No content provider: " + uri);
- }
- IContentProvider stableProvider = null;
- AssetFileDescriptor fd = null;
- try {
- ICancellationSignal remoteCancellationSignal = null;
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
- remoteCancellationSignal = unstableProvider.createCancellationSignal();
- cancellationSignal.setRemote(remoteCancellationSignal);
- }
- try {
- fd = unstableProvider.openTypedAssetFile(
- mPackageName, uri, mimeType, opts, remoteCancellationSignal);
- if (fd == null) {
- // The provider will be released by the finally{} clause
- return null;
- }
- } catch (DeadObjectException e) {
- // The remote process has died... but we only hold an unstable
- // reference though, so we might recover!!! Let's try!!!!
- // This is exciting!!1!!1!!!!1
- unstableProviderDied(unstableProvider);
- stableProvider = acquireProvider(uri);
- if (stableProvider == null) {
- throw new FileNotFoundException("No content provider: " + uri);
- }
- fd = stableProvider.openTypedAssetFile(
- mPackageName, uri, mimeType, opts, remoteCancellationSignal);
- if (fd == null) {
- // The provider will be released by the finally{} clause
- return null;
- }
- }
- if (stableProvider == null) {
- stableProvider = acquireProvider(uri);
- }
- releaseUnstableProvider(unstableProvider);
- ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
- fd.getParcelFileDescriptor(), stableProvider);
- // Success! Don't release the provider when exiting, let
- // ParcelFileDescriptorInner do that