summaryrefslogtreecommitdiffstats
path: root/core/java/android/content/ContentResolver.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/content/ContentResolver.java')
-rw-r--r--core/java/android/content/ContentResolver.java784
1 files changed, 784 insertions, 0 deletions
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
new file mode 100644
index 0000000..0d886ee
--- /dev/null
+++ b/core/java/android/content/ContentResolver.java
@@ -0,0 +1,784 @@
+/*
+ * 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.NameNotFoundException;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.database.IContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.text.TextUtils;
+
+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.List;
+
+
+/**
+ * This class provides applications access to the content model.
+ */
+public abstract class ContentResolver {
+ public final static String SYNC_EXTRAS_ACCOUNT = "account";
+ public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
+ public static final String SYNC_EXTRAS_FORCE = "force";
+ public static final String SYNC_EXTRAS_UPLOAD = "upload";
+ public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
+ public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
+
+ public static final String SCHEME_CONTENT = "content";
+ public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
+ public static final String SCHEME_FILE = "file";
+
+ /**
+ * 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";
+
+ public ContentResolver(Context context)
+ {
+ mContext = context;
+ }
+
+ /** @hide */
+ protected abstract IContentProvider acquireProvider(Context c, String name);
+ /** @hide */
+ public abstract boolean releaseProvider(IContentProvider icp);
+
+ /**
+ * 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)
+ {
+ IContentProvider provider = acquireProvider(url);
+ if (provider == null) {
+ return null;
+ }
+ try {
+ return provider.getType(url);
+ } catch (RemoteException e) {
+ return null;
+ } catch (java.lang.Exception e) {
+ return null;
+ } finally {
+ releaseProvider(provider);
+ }
+ }
+
+ /**
+ * Query the given URI, returning a {@link Cursor} over the result set.
+ *
+ * @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 discouraged to prevent reading data
+ * from storage that isn't going to be used.
+ * @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) {
+ IContentProvider provider = acquireProvider(uri);
+ if (provider == null) {
+ return null;
+ }
+ try {
+ Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder);
+ if(qCursor == null) {
+ releaseProvider(provider);
+ return null;
+ }
+ //Wrap the cursor object into CursorWrapperInner object
+ return new CursorWrapperInner(qCursor, provider);
+ } catch (RemoteException e) {
+ releaseProvider(provider);
+ return null;
+ } catch(RuntimeException e) {
+ releaseProvider(provider);
+ throw e;
+ }
+ }
+
+ /**
+ * 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");
+ 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);
+ 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 "content:" 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.
+ *
+ * @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 of 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 {
+ AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode);
+ 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 "content:" URI. This
+ * interacts with the underlying {@link ContentProvider#openAssetFile}
+ * 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>
+ *
+ * @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 {
+ 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()), modeToMode(uri, mode));
+ return new AssetFileDescriptor(pfd, 0, -1);
+ } else {
+ IContentProvider provider = acquireProvider(uri);
+ if (provider == null) {
+ throw new FileNotFoundException("No content provider: " + uri);
+ }
+ try {
+ AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
+ if(fd == null) {
+ releaseProvider(provider);
+ return null;
+ }
+ ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+ fd.getParcelFileDescriptor(), provider);
+ return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+ fd.getDeclaredLength());
+ } catch (RemoteException e) {
+ releaseProvider(provider);
+ throw new FileNotFoundException("Dead content provider: " + uri);
+ } catch (FileNotFoundException e) {
+ releaseProvider(provider);
+ throw e;
+ } catch (RuntimeException e) {
+ releaseProvider(provider);
+ throw e;
+ }
+ }
+ }
+
+ class OpenResourceIdResult {
+ Resources r;
+ int id;
+ }
+
+ OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException {
+ String authority = uri.getAuthority();
+ Resources r;
+ if (TextUtils.isEmpty(authority)) {
+ throw new FileNotFoundException("No authority: " + uri);
+ } else {
+ try {
+ r = mContext.getPackageManager().getResourcesForApplication(authority);
+ } catch (NameNotFoundException ex) {
+ throw new FileNotFoundException("No package found for authority: " + uri);
+ }
+ }
+ List<String> path = uri.getPathSegments();
+ if (path == null) {
+ throw new FileNotFoundException("No path: " + uri);
+ }
+ int len = path.size();
+ int id;
+ if (len == 1) {
+ try {
+ id = Integer.parseInt(path.get(0));
+ } catch (NumberFormatException e) {
+ throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
+ }
+ } else if (len == 2) {
+ id = r.getIdentifier(path.get(1), path.get(0), authority);
+ } else {
+ throw new FileNotFoundException("More than two path segments: " + uri);
+ }
+ if (id == 0) {
+ throw new FileNotFoundException("No resource found for: " + uri);
+ }
+ OpenResourceIdResult res = new OpenResourceIdResult();
+ res.r = r;
+ res.id = id;
+ return res;
+ }
+
+ /** @hide */
+ static public int modeToMode(Uri uri, String mode) throws FileNotFoundException {
+ int modeBits;
+ if ("r".equals(mode)) {
+ modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
+ } else if ("w".equals(mode) || "wt".equals(mode)) {
+ modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
+ | ParcelFileDescriptor.MODE_CREATE
+ | ParcelFileDescriptor.MODE_TRUNCATE;
+ } else if ("wa".equals(mode)) {
+ modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
+ | ParcelFileDescriptor.MODE_CREATE
+ | ParcelFileDescriptor.MODE_APPEND;
+ } else if ("rw".equals(mode)) {
+ modeBits = ParcelFileDescriptor.MODE_READ_WRITE
+ | ParcelFileDescriptor.MODE_CREATE;
+ } else if ("rwt".equals(mode)) {
+ modeBits = ParcelFileDescriptor.MODE_READ_WRITE
+ | ParcelFileDescriptor.MODE_CREATE
+ | ParcelFileDescriptor.MODE_TRUNCATE;
+ } else {
+ throw new FileNotFoundException("Bad mode for " + uri + ": "
+ + mode);
+ }
+ return modeBits;
+ }
+
+ /**
+ * Inserts a row into a table at the given URL.
+ *
+ * If the content provider supports transactions the insertion will be atomic.
+ *
+ * @param url The URL of the table to insert into.
+ * @param values The initial values for the newly inserted row. The key is the column name for
+ * the field. Passing an empty ContentValues will create an empty row.
+ * @return the URL of the newly created row.
+ */
+ public final Uri insert(Uri url, ContentValues values)
+ {
+ IContentProvider provider = acquireProvider(url);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+ try {
+ return provider.insert(url, values);
+ } catch (RemoteException e) {
+ return null;
+ } finally {
+ releaseProvider(provider);
+ }
+ }
+
+ /**
+ * Inserts multiple rows into a table at the given URL.
+ *
+ * This function make no guarantees about the atomicity of the insertions.
+ *
+ * @param url The URL of the table to insert into.
+ * @param values The initial values for the newly inserted rows. The key is the column name for
+ * the field. Passing null will create an empty row.
+ * @return the number of newly created rows.
+ */
+ public final int bulkInsert(Uri url, ContentValues[] values)
+ {
+ IContentProvider provider = acquireProvider(url);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+ try {
+ return provider.bulkInsert(url, values);
+ } catch (RemoteException e) {
+ return 0;
+ } finally {
+ releaseProvider(provider);
+ }
+ }
+
+ /**
+ * Deletes row(s) specified by a content URI.
+ *
+ * If the content provider supports transactions, the deletion will be atomic.
+ *
+ * @param url The URL of the row to delete.
+ * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause
+ (excluding the WHERE itself).
+ * @return The number of rows deleted.
+ */
+ public final int delete(Uri url, String where, String[] selectionArgs)
+ {
+ IContentProvider provider = acquireProvider(url);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URL " + url);
+ }
+ try {
+ return provider.delete(url, where, selectionArgs);
+ } catch (RemoteException e) {
+ return -1;
+ } finally {
+ releaseProvider(provider);
+ }
+ }
+
+ /**
+ * Update row(s) in a content URI.
+ *
+ * If the content provider supports transactions the update will be atomic.
+ *
+ * @param uri The URI to modify.
+ * @param values The new field values. The key is the column name for the field.
+ A null value will remove an existing field value.
+ * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause
+ (excluding the WHERE itself).
+ * @return the URL of the newly created row
+ * @throws NullPointerException if uri or values are null
+ */
+ public final int update(Uri uri, ContentValues values, String where,
+ String[] selectionArgs) {
+ IContentProvider provider = acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ return provider.update(uri, values, where, selectionArgs);
+ } catch (RemoteException e) {
+ return -1;
+ } finally {
+ releaseProvider(provider);
+ }
+ }
+
+ /**
+ * Returns the content provider for the given content URI..
+ *
+ * @param uri The URI to a content provider
+ * @return The ContentProvider for the given URI, or null if no content provider is found.
+ * @hide
+ */
+ public final IContentProvider acquireProvider(Uri uri)
+ {
+ if (!SCHEME_CONTENT.equals(uri.getScheme())) {
+ return null;
+ }
+ String auth = uri.getAuthority();
+ if (auth != null) {
+ return acquireProvider(mContext, uri.getAuthority());
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public final IContentProvider acquireProvider(String name) {
+ if(name == null) {
+ return null;
+ }
+ return acquireProvider(mContext, name);
+ }
+
+ /**
+ * Register an observer class that gets callbacks when data identified by a
+ * given content URI changes.
+ *
+ * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI
+ * for a whole class of content.
+ * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code>
+ * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI
+ * specified by <em>uri</em> will cause notifications to be sent. If true, than any URI values
+ * at or below the specified URI will also trigger a match.
+ * @param observer The object that receives callbacks when changes occur.
+ * @see #unregisterContentObserver
+ */
+ public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
+ ContentObserver observer)
+ {
+ try {
+ ContentServiceNative.getDefault().registerContentObserver(uri, notifyForDescendents,
+ observer.getContentObserver());
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Unregisters a change observer.
+ *
+ * @param observer The previously registered observer that is no longer needed.
+ * @see #registerContentObserver
+ */
+ public final void unregisterContentObserver(ContentObserver observer) {
+ try {
+ IContentObserver contentObserver = observer.releaseContentObserver();
+ if (contentObserver != null) {
+ ContentServiceNative.getDefault().unregisterContentObserver(
+ contentObserver);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Notify registered observers that a row was updated.
+ * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
+ * By default, CursorAdapter objects will get this notification.
+ *
+ * @param uri
+ * @param observer The observer that originated the change, may be <code>null</null>
+ */
+ public void notifyChange(Uri uri, ContentObserver observer) {
+ notifyChange(uri, observer, true /* sync to network */);
+ }
+
+ /**
+ * Notify registered observers that a row was updated.
+ * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
+ * By default, CursorAdapter objects will get this notification.
+ *
+ * @param uri
+ * @param observer The observer that originated the change, may be <code>null</null>
+ * @param syncToNetwork If true, attempt to sync the change to the network.
+ */
+ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+ try {
+ ContentServiceNative.getDefault().notifyChange(
+ uri, observer == null ? null : observer.getContentObserver(),
+ observer != null && observer.deliverSelfNotifications(), syncToNetwork);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Start an asynchronous sync operation. If you want to monitor the progress
+ * of the sync you may register a SyncObserver. Only values of the following
+ * types may be used in the extras bundle:
+ * <ul>
+ * <li>Integer</li>
+ * <li>Long</li>
+ * <li>Boolean</li>
+ * <li>Float</li>
+ * <li>Double</li>
+ * <li>String</li>
+ * </ul>
+ *
+ * @param uri the uri of the provider to sync or null to sync all providers.
+ * @param extras any extras to pass to the SyncAdapter.
+ */
+ public void startSync(Uri uri, Bundle extras) {
+ validateSyncExtrasBundle(extras);
+ try {
+ ContentServiceNative.getDefault().startSync(uri, extras);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Check that only values of the following types are in the Bundle:
+ * <ul>
+ * <li>Integer</li>
+ * <li>Long</li>
+ * <li>Boolean</li>
+ * <li>Float</li>
+ * <li>Double</li>
+ * <li>String</li>
+ * <li>null</li>
+ * </ul>
+ * @param extras the Bundle to check
+ */
+ public static void validateSyncExtrasBundle(Bundle extras) {
+ try {
+ for (String key : extras.keySet()) {
+ Object value = extras.get(key);
+ if (value == null) continue;
+ if (value instanceof Long) continue;
+ if (value instanceof Integer) continue;
+ if (value instanceof Boolean) continue;
+ if (value instanceof Float) continue;
+ if (value instanceof Double) continue;
+ if (value instanceof String) continue;
+ throw new IllegalArgumentException("unexpected value type: "
+ + value.getClass().getName());
+ }
+ } catch (IllegalArgumentException e) {
+ throw e;
+ } catch (RuntimeException exc) {
+ throw new IllegalArgumentException("error unparceling Bundle", exc);
+ }
+ }
+
+ public void cancelSync(Uri uri) {
+ try {
+ ContentServiceNative.getDefault().cancelSync(uri);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private final class CursorWrapperInner extends CursorWrapper {
+ private IContentProvider mContentProvider;
+ public static final String TAG="CursorWrapperInner";
+ private boolean mCloseFlag = false;
+
+ CursorWrapperInner(Cursor cursor, IContentProvider icp) {
+ super(cursor);
+ mContentProvider = icp;
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ ContentResolver.this.releaseProvider(mContentProvider);
+ mCloseFlag = true;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if(!mCloseFlag) {
+ ContentResolver.this.releaseProvider(mContentProvider);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+
+ private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
+ private IContentProvider mContentProvider;
+ public static final String TAG="ParcelFileDescriptorInner";
+ private boolean mReleaseProviderFlag = false;
+
+ ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
+ super(pfd);
+ mContentProvider = icp;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if(!mReleaseProviderFlag) {
+ super.close();
+ ContentResolver.this.releaseProvider(mContentProvider);
+ mReleaseProviderFlag = true;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (!mReleaseProviderFlag) {
+ close();
+ }
+ }
+ }
+
+ private final Context mContext;
+ private static final String TAG = "ContentResolver";
+}