ContentResolver.java revision 4c87a3f26a7c0c75fa371024a8726b59a108fd0f
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.content;
18
19import android.content.pm.PackageManager.NameNotFoundException;
20import android.content.res.AssetFileDescriptor;
21import android.content.res.Resources;
22import android.database.ContentObserver;
23import android.database.Cursor;
24import android.database.CursorWrapper;
25import android.database.IContentObserver;
26import android.net.Uri;
27import android.os.Bundle;
28import android.os.IBinder;
29import android.os.ParcelFileDescriptor;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.text.TextUtils;
33import android.accounts.Account;
34import android.util.Config;
35import android.util.Log;
36
37import java.io.File;
38import java.io.FileInputStream;
39import java.io.FileNotFoundException;
40import java.io.IOException;
41import java.io.InputStream;
42import java.io.OutputStream;
43import java.util.List;
44import java.util.ArrayList;
45
46
47/**
48 * This class provides applications access to the content model.
49 */
50public abstract class ContentResolver {
51    /**
52     * @deprecated instead use
53     * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
54     */
55    @Deprecated
56    public static final String SYNC_EXTRAS_ACCOUNT = "account";
57    public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
58    /**
59     * @deprecated instead use
60     * {@link #SYNC_EXTRAS_MANUAL}
61     */
62    @Deprecated
63    public static final String SYNC_EXTRAS_FORCE = "force";
64    public static final String SYNC_EXTRAS_MANUAL = "force";
65    public static final String SYNC_EXTRAS_UPLOAD = "upload";
66    public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
67    public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
68
69    /**
70     * Set by the SyncManager to request that the SyncAdapter initialize itself for
71     * the given account/authority pair. One required initialization step is to
72     * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
73     * called with a >= 0 value. When this flag is set the SyncAdapter does not need to
74     * do a full sync, though it is allowed to do so.
75     */
76    public static final String SYNC_EXTRAS_INITIALIZE = "initialize";
77
78    public static final String SCHEME_CONTENT = "content";
79    public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
80    public static final String SCHEME_FILE = "file";
81
82    /**
83     * This is the Android platform's base MIME type for a content: URI
84     * containing a Cursor of a single item.  Applications should use this
85     * as the base type along with their own sub-type of their content: URIs
86     * that represent a particular item.  For example, hypothetical IMAP email
87     * client may have a URI
88     * <code>content://com.company.provider.imap/inbox/1</code> for a particular
89     * message in the inbox, whose MIME type would be reported as
90     * <code>CURSOR_ITEM_BASE_TYPE + "/vnd.company.imap-msg"</code>
91     *
92     * <p>Compare with {@link #CURSOR_DIR_BASE_TYPE}.
93     */
94    public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
95
96    /**
97     * This is the Android platform's base MIME type for a content: URI
98     * containing a Cursor of zero or more items.  Applications should use this
99     * as the base type along with their own sub-type of their content: URIs
100     * that represent a directory of items.  For example, hypothetical IMAP email
101     * client may have a URI
102     * <code>content://com.company.provider.imap/inbox</code> for all of the
103     * messages in its inbox, whose MIME type would be reported as
104     * <code>CURSOR_DIR_BASE_TYPE + "/vnd.company.imap-msg"</code>
105     *
106     * <p>Note how the base MIME type varies between this and
107     * {@link #CURSOR_ITEM_BASE_TYPE} depending on whether there is
108     * one single item or multiple items in the data set, while the sub-type
109     * remains the same because in either case the data structure contained
110     * in the cursor is the same.
111     */
112    public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
113
114    /** @hide */
115    public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
116    /** @hide */
117    public static final int SYNC_ERROR_AUTHENTICATION = 2;
118    /** @hide */
119    public static final int SYNC_ERROR_IO = 3;
120    /** @hide */
121    public static final int SYNC_ERROR_PARSE = 4;
122    /** @hide */
123    public static final int SYNC_ERROR_CONFLICT = 5;
124    /** @hide */
125    public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6;
126    /** @hide */
127    public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7;
128    /** @hide */
129    public static final int SYNC_ERROR_INTERNAL = 8;
130
131    /** @hide */
132    public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0;
133    /** @hide */
134    public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1;
135    /** @hide */
136    public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2;
137    /** @hide */
138    public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3;
139    /** @hide */
140    public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
141
142    public ContentResolver(Context context) {
143        mContext = context;
144    }
145
146    /** @hide */
147    protected abstract IContentProvider acquireProvider(Context c, String name);
148    /** @hide */
149    public abstract boolean releaseProvider(IContentProvider icp);
150
151    /**
152     * Return the MIME type of the given content URL.
153     *
154     * @param url A Uri identifying content (either a list or specific type),
155     * using the content:// scheme.
156     * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
157     */
158    public final String getType(Uri url)
159    {
160        IContentProvider provider = acquireProvider(url);
161        if (provider == null) {
162            return null;
163        }
164        try {
165            return provider.getType(url);
166        } catch (RemoteException e) {
167            return null;
168        } catch (java.lang.Exception e) {
169            return null;
170        } finally {
171            releaseProvider(provider);
172        }
173    }
174
175    /**
176     * Query the given URI, returning a {@link Cursor} over the result set.
177     *
178     * @param uri The URI, using the content:// scheme, for the content to
179     *         retrieve.
180     * @param projection A list of which columns to return. Passing null will
181     *         return all columns, which is discouraged to prevent reading data
182     *         from storage that isn't going to be used.
183     * @param selection A filter declaring which rows to return, formatted as an
184     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
185     *         return all rows for the given URI.
186     * @param selectionArgs You may include ?s in selection, which will be
187     *         replaced by the values from selectionArgs, in the order that they
188     *         appear in the selection. The values will be bound as Strings.
189     * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
190     *         clause (excluding the ORDER BY itself). Passing null will use the
191     *         default sort order, which may be unordered.
192     * @return A Cursor object, which is positioned before the first entry, or null
193     * @see Cursor
194     */
195    public final Cursor query(Uri uri, String[] projection,
196            String selection, String[] selectionArgs, String sortOrder) {
197        IContentProvider provider = acquireProvider(uri);
198        if (provider == null) {
199            return null;
200        }
201        try {
202            Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder);
203            if(qCursor == null) {
204                releaseProvider(provider);
205                return null;
206            }
207            //Wrap the cursor object into CursorWrapperInner object
208            return new CursorWrapperInner(qCursor, provider);
209        } catch (RemoteException e) {
210            releaseProvider(provider);
211            return null;
212        } catch(RuntimeException e) {
213            releaseProvider(provider);
214            throw e;
215        }
216    }
217
218    /**
219     * EntityIterator wrapper that releases the associated ContentProviderClient when the
220     * iterator is closed.
221     */
222    private class EntityIteratorWrapper implements EntityIterator {
223        private final EntityIterator mInner;
224        private final ContentProviderClient mClient;
225        private volatile boolean mClientReleased;
226
227        EntityIteratorWrapper(EntityIterator inner, ContentProviderClient client) {
228            mInner = inner;
229            mClient = client;
230            mClientReleased = false;
231        }
232
233        public boolean hasNext() throws RemoteException {
234            if (mClientReleased) {
235                throw new IllegalStateException("this iterator is already closed");
236            }
237            return mInner.hasNext();
238        }
239
240        public Entity next() throws RemoteException {
241            if (mClientReleased) {
242                throw new IllegalStateException("this iterator is already closed");
243            }
244            return mInner.next();
245        }
246
247        public void reset() throws RemoteException {
248            if (mClientReleased) {
249                throw new IllegalStateException("this iterator is already closed");
250            }
251            mInner.reset();
252        }
253
254        public void close() {
255            mClient.release();
256            mInner.close();
257            mClientReleased = true;
258        }
259
260        protected void finalize() throws Throwable {
261            if (!mClientReleased) {
262                mClient.release();
263            }
264            super.finalize();
265        }
266    }
267
268    /**
269     * Query the given URI, returning an {@link EntityIterator} over the result set.
270     *
271     * @param uri The URI, using the content:// scheme, for the content to
272     *         retrieve.
273     * @param selection A filter declaring which rows to return, formatted as an
274     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
275     *         return all rows for the given URI.
276     * @param selectionArgs You may include ?s in selection, which will be
277     *         replaced by the values from selectionArgs, in the order that they
278     *         appear in the selection. The values will be bound as Strings.
279     * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
280     *         clause (excluding the ORDER BY itself). Passing null will use the
281     *         default sort order, which may be unordered.
282     * @return An EntityIterator object
283     * @throws RemoteException thrown if a RemoteException is encountered while attempting
284     *   to communicate with a remote provider.
285     * @throws IllegalArgumentException thrown if there is no provider that matches the uri
286     */
287    public final EntityIterator queryEntities(Uri uri,
288            String selection, String[] selectionArgs, String sortOrder) throws RemoteException {
289        ContentProviderClient provider = acquireContentProviderClient(uri);
290        if (provider == null) {
291            throw new IllegalArgumentException("Unknown URL " + uri);
292        }
293        try {
294            EntityIterator entityIterator =
295                    provider.queryEntities(uri, selection, selectionArgs, sortOrder);
296            return new EntityIteratorWrapper(entityIterator, provider);
297        } catch(RuntimeException e) {
298            provider.release();
299            throw e;
300        } catch(RemoteException e) {
301            provider.release();
302            throw e;
303        }
304    }
305
306    /**
307     * Open a stream on to the content associated with a content URI.  If there
308     * is no data associated with the URI, FileNotFoundException is thrown.
309     *
310     * <h5>Accepts the following URI schemes:</h5>
311     * <ul>
312     * <li>content ({@link #SCHEME_CONTENT})</li>
313     * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
314     * <li>file ({@link #SCHEME_FILE})</li>
315     * </ul>
316     *
317     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
318     * on these schemes.
319     *
320     * @param uri The desired URI.
321     * @return InputStream
322     * @throws FileNotFoundException if the provided URI could not be opened.
323     * @see #openAssetFileDescriptor(Uri, String)
324     */
325    public final InputStream openInputStream(Uri uri)
326            throws FileNotFoundException {
327        String scheme = uri.getScheme();
328        if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
329            // Note: left here to avoid breaking compatibility.  May be removed
330            // with sufficient testing.
331            OpenResourceIdResult r = getResourceId(uri);
332            try {
333                InputStream stream = r.r.openRawResource(r.id);
334                return stream;
335            } catch (Resources.NotFoundException ex) {
336                throw new FileNotFoundException("Resource does not exist: " + uri);
337            }
338        } else if (SCHEME_FILE.equals(scheme)) {
339            // Note: left here to avoid breaking compatibility.  May be removed
340            // with sufficient testing.
341            return new FileInputStream(uri.getPath());
342        } else {
343            AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r");
344            try {
345                return fd != null ? fd.createInputStream() : null;
346            } catch (IOException e) {
347                throw new FileNotFoundException("Unable to create stream");
348            }
349        }
350    }
351
352    /**
353     * Synonym for {@link #openOutputStream(Uri, String)
354     * openOutputStream(uri, "w")}.
355     * @throws FileNotFoundException if the provided URI could not be opened.
356     */
357    public final OutputStream openOutputStream(Uri uri)
358            throws FileNotFoundException {
359        return openOutputStream(uri, "w");
360    }
361
362    /**
363     * Open a stream on to the content associated with a content URI.  If there
364     * is no data associated with the URI, FileNotFoundException is thrown.
365     *
366     * <h5>Accepts the following URI schemes:</h5>
367     * <ul>
368     * <li>content ({@link #SCHEME_CONTENT})</li>
369     * <li>file ({@link #SCHEME_FILE})</li>
370     * </ul>
371     *
372     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
373     * on these schemes.
374     *
375     * @param uri The desired URI.
376     * @param mode May be "w", "wa", "rw", or "rwt".
377     * @return OutputStream
378     * @throws FileNotFoundException if the provided URI could not be opened.
379     * @see #openAssetFileDescriptor(Uri, String)
380     */
381    public final OutputStream openOutputStream(Uri uri, String mode)
382            throws FileNotFoundException {
383        AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode);
384        try {
385            return fd != null ? fd.createOutputStream() : null;
386        } catch (IOException e) {
387            throw new FileNotFoundException("Unable to create stream");
388        }
389    }
390
391    /**
392     * Open a raw file descriptor to access data under a "content:" URI.  This
393     * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
394     * underlying {@link ContentProvider#openFile}
395     * ContentProvider.openFile()} method, so will <em>not</em> work with
396     * providers that return sub-sections of files.  If at all possible,
397     * you should use {@link #openAssetFileDescriptor(Uri, String)}.  You
398     * will receive a FileNotFoundException exception if the provider returns a
399     * sub-section of a file.
400     *
401     * <h5>Accepts the following URI schemes:</h5>
402     * <ul>
403     * <li>content ({@link #SCHEME_CONTENT})</li>
404     * <li>file ({@link #SCHEME_FILE})</li>
405     * </ul>
406     *
407     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
408     * on these schemes.
409     *
410     * @param uri The desired URI to open.
411     * @param mode The file mode to use, as per {@link ContentProvider#openFile
412     * ContentProvider.openFile}.
413     * @return Returns a new ParcelFileDescriptor pointing to the file.  You
414     * own this descriptor and are responsible for closing it when done.
415     * @throws FileNotFoundException Throws FileNotFoundException of no
416     * file exists under the URI or the mode is invalid.
417     * @see #openAssetFileDescriptor(Uri, String)
418     */
419    public final ParcelFileDescriptor openFileDescriptor(Uri uri,
420            String mode) throws FileNotFoundException {
421        AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode);
422        if (afd == null) {
423            return null;
424        }
425
426        if (afd.getDeclaredLength() < 0) {
427            // This is a full file!
428            return afd.getParcelFileDescriptor();
429        }
430
431        // Client can't handle a sub-section of a file, so close what
432        // we got and bail with an exception.
433        try {
434            afd.close();
435        } catch (IOException e) {
436        }
437
438        throw new FileNotFoundException("Not a whole file");
439    }
440
441    /**
442     * Open a raw file descriptor to access data under a "content:" URI.  This
443     * interacts with the underlying {@link ContentProvider#openAssetFile}
444     * ContentProvider.openAssetFile()} method of the provider associated with the
445     * given URI, to retrieve any file stored there.
446     *
447     * <h5>Accepts the following URI schemes:</h5>
448     * <ul>
449     * <li>content ({@link #SCHEME_CONTENT})</li>
450     * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
451     * <li>file ({@link #SCHEME_FILE})</li>
452     * </ul>
453     * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
454     * <p>
455     * A Uri object can be used to reference a resource in an APK file.  The
456     * Uri should be one of the following formats:
457     * <ul>
458     * <li><code>android.resource://package_name/id_number</code><br/>
459     * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
460     * For example <code>com.example.myapp</code><br/>
461     * <code>id_number</code> is the int form of the ID.<br/>
462     * The easiest way to construct this form is
463     * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
464     * </li>
465     * <li><code>android.resource://package_name/type/name</code><br/>
466     * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
467     * For example <code>com.example.myapp</code><br/>
468     * <code>type</code> is the string form of the resource type.  For example, <code>raw</code>
469     * or <code>drawable</code>.
470     * <code>name</code> is the string form of the resource name.  That is, whatever the file
471     * name was in your res directory, without the type extension.
472     * The easiest way to construct this form is
473     * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
474     * </li>
475     * </ul>
476     *
477     * @param uri The desired URI to open.
478     * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
479     * ContentProvider.openAssetFile}.
480     * @return Returns a new ParcelFileDescriptor pointing to the file.  You
481     * own this descriptor and are responsible for closing it when done.
482     * @throws FileNotFoundException Throws FileNotFoundException of no
483     * file exists under the URI or the mode is invalid.
484     */
485    public final AssetFileDescriptor openAssetFileDescriptor(Uri uri,
486            String mode) throws FileNotFoundException {
487        String scheme = uri.getScheme();
488        if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
489            if (!"r".equals(mode)) {
490                throw new FileNotFoundException("Can't write resources: " + uri);
491            }
492            OpenResourceIdResult r = getResourceId(uri);
493            try {
494                return r.r.openRawResourceFd(r.id);
495            } catch (Resources.NotFoundException ex) {
496                throw new FileNotFoundException("Resource does not exist: " + uri);
497            }
498        } else if (SCHEME_FILE.equals(scheme)) {
499            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
500                    new File(uri.getPath()), modeToMode(uri, mode));
501            return new AssetFileDescriptor(pfd, 0, -1);
502        } else {
503            IContentProvider provider = acquireProvider(uri);
504            if (provider == null) {
505                throw new FileNotFoundException("No content provider: " + uri);
506            }
507            try {
508                AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
509                if(fd == null) {
510                    releaseProvider(provider);
511                    return null;
512                }
513                ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
514                        fd.getParcelFileDescriptor(), provider);
515                return new AssetFileDescriptor(pfd, fd.getStartOffset(),
516                        fd.getDeclaredLength());
517            } catch (RemoteException e) {
518                releaseProvider(provider);
519                throw new FileNotFoundException("Dead content provider: " + uri);
520            } catch (FileNotFoundException e) {
521                releaseProvider(provider);
522                throw e;
523            } catch (RuntimeException e) {
524                releaseProvider(provider);
525                throw e;
526            }
527        }
528    }
529
530    /**
531     * A resource identified by the {@link Resources} that contains it, and a resource id.
532     *
533     * @hide
534     */
535    public class OpenResourceIdResult {
536        public Resources r;
537        public int id;
538    }
539
540    /**
541     * Resolves an android.resource URI to a {@link Resources} and a resource id.
542     *
543     * @hide
544     */
545    public OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException {
546        String authority = uri.getAuthority();
547        Resources r;
548        if (TextUtils.isEmpty(authority)) {
549            throw new FileNotFoundException("No authority: " + uri);
550        } else {
551            try {
552                r = mContext.getPackageManager().getResourcesForApplication(authority);
553            } catch (NameNotFoundException ex) {
554                throw new FileNotFoundException("No package found for authority: " + uri);
555            }
556        }
557        List<String> path = uri.getPathSegments();
558        if (path == null) {
559            throw new FileNotFoundException("No path: " + uri);
560        }
561        int len = path.size();
562        int id;
563        if (len == 1) {
564            try {
565                id = Integer.parseInt(path.get(0));
566            } catch (NumberFormatException e) {
567                throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
568            }
569        } else if (len == 2) {
570            id = r.getIdentifier(path.get(1), path.get(0), authority);
571        } else {
572            throw new FileNotFoundException("More than two path segments: " + uri);
573        }
574        if (id == 0) {
575            throw new FileNotFoundException("No resource found for: " + uri);
576        }
577        OpenResourceIdResult res = new OpenResourceIdResult();
578        res.r = r;
579        res.id = id;
580        return res;
581    }
582
583    /** @hide */
584    static public int modeToMode(Uri uri, String mode) throws FileNotFoundException {
585        int modeBits;
586        if ("r".equals(mode)) {
587            modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
588        } else if ("w".equals(mode) || "wt".equals(mode)) {
589            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
590                    | ParcelFileDescriptor.MODE_CREATE
591                    | ParcelFileDescriptor.MODE_TRUNCATE;
592        } else if ("wa".equals(mode)) {
593            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
594                    | ParcelFileDescriptor.MODE_CREATE
595                    | ParcelFileDescriptor.MODE_APPEND;
596        } else if ("rw".equals(mode)) {
597            modeBits = ParcelFileDescriptor.MODE_READ_WRITE
598                    | ParcelFileDescriptor.MODE_CREATE;
599        } else if ("rwt".equals(mode)) {
600            modeBits = ParcelFileDescriptor.MODE_READ_WRITE
601                    | ParcelFileDescriptor.MODE_CREATE
602                    | ParcelFileDescriptor.MODE_TRUNCATE;
603        } else {
604            throw new FileNotFoundException("Bad mode for " + uri + ": "
605                    + mode);
606        }
607        return modeBits;
608    }
609
610    /**
611     * Inserts a row into a table at the given URL.
612     *
613     * If the content provider supports transactions the insertion will be atomic.
614     *
615     * @param url The URL of the table to insert into.
616     * @param values The initial values for the newly inserted row. The key is the column name for
617     *               the field. Passing an empty ContentValues will create an empty row.
618     * @return the URL of the newly created row.
619     */
620    public final Uri insert(Uri url, ContentValues values)
621    {
622        IContentProvider provider = acquireProvider(url);
623        if (provider == null) {
624            throw new IllegalArgumentException("Unknown URL " + url);
625        }
626        try {
627            return provider.insert(url, values);
628        } catch (RemoteException e) {
629            return null;
630        } finally {
631            releaseProvider(provider);
632        }
633    }
634
635    /**
636     * Applies each of the {@link ContentProviderOperation} objects and returns an array
637     * of their results. Passes through OperationApplicationException, which may be thrown
638     * by the call to {@link ContentProviderOperation#apply}.
639     * If all the applications succeed then a {@link ContentProviderResult} array with the
640     * same number of elements as the operations will be returned. It is implementation-specific
641     * how many, if any, operations will have been successfully applied if a call to
642     * apply results in a {@link OperationApplicationException}.
643     * @param authority the authority of the ContentProvider to which this batch should be applied
644     * @param operations the operations to apply
645     * @return the results of the applications
646     * @throws OperationApplicationException thrown if an application fails.
647     * See {@link ContentProviderOperation#apply} for more information.
648     * @throws RemoteException thrown if a RemoteException is encountered while attempting
649     *   to communicate with a remote provider.
650     */
651    public ContentProviderResult[] applyBatch(String authority,
652            ArrayList<ContentProviderOperation> operations)
653            throws RemoteException, OperationApplicationException {
654        ContentProviderClient provider = acquireContentProviderClient(authority);
655        if (provider == null) {
656            throw new IllegalArgumentException("Unknown authority " + authority);
657        }
658        try {
659            return provider.applyBatch(operations);
660        } finally {
661            provider.release();
662        }
663    }
664
665    /**
666     * Inserts multiple rows into a table at the given URL.
667     *
668     * This function make no guarantees about the atomicity of the insertions.
669     *
670     * @param url The URL of the table to insert into.
671     * @param values The initial values for the newly inserted rows. The key is the column name for
672     *               the field. Passing null will create an empty row.
673     * @return the number of newly created rows.
674     */
675    public final int bulkInsert(Uri url, ContentValues[] values)
676    {
677        IContentProvider provider = acquireProvider(url);
678        if (provider == null) {
679            throw new IllegalArgumentException("Unknown URL " + url);
680        }
681        try {
682            return provider.bulkInsert(url, values);
683        } catch (RemoteException e) {
684            return 0;
685        } finally {
686            releaseProvider(provider);
687        }
688    }
689
690    /**
691     * Deletes row(s) specified by a content URI.
692     *
693     * If the content provider supports transactions, the deletion will be atomic.
694     *
695     * @param url The URL of the row to delete.
696     * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause
697                    (excluding the WHERE itself).
698     * @return The number of rows deleted.
699     */
700    public final int delete(Uri url, String where, String[] selectionArgs)
701    {
702        IContentProvider provider = acquireProvider(url);
703        if (provider == null) {
704            throw new IllegalArgumentException("Unknown URL " + url);
705        }
706        try {
707            return provider.delete(url, where, selectionArgs);
708        } catch (RemoteException e) {
709            return -1;
710        } finally {
711            releaseProvider(provider);
712        }
713    }
714
715    /**
716     * Update row(s) in a content URI.
717     *
718     * If the content provider supports transactions the update will be atomic.
719     *
720     * @param uri The URI to modify.
721     * @param values The new field values. The key is the column name for the field.
722                     A null value will remove an existing field value.
723     * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause
724                    (excluding the WHERE itself).
725     * @return The number of rows updated.
726     * @throws NullPointerException if uri or values are null
727     */
728    public final int update(Uri uri, ContentValues values, String where,
729            String[] selectionArgs) {
730        IContentProvider provider = acquireProvider(uri);
731        if (provider == null) {
732            throw new IllegalArgumentException("Unknown URI " + uri);
733        }
734        try {
735            return provider.update(uri, values, where, selectionArgs);
736        } catch (RemoteException e) {
737            return -1;
738        } finally {
739            releaseProvider(provider);
740        }
741    }
742
743    /**
744     * Returns the content provider for the given content URI..
745     *
746     * @param uri The URI to a content provider
747     * @return The ContentProvider for the given URI, or null if no content provider is found.
748     * @hide
749     */
750    public final IContentProvider acquireProvider(Uri uri)
751    {
752        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
753            return null;
754        }
755        String auth = uri.getAuthority();
756        if (auth != null) {
757            return acquireProvider(mContext, uri.getAuthority());
758        }
759        return null;
760    }
761
762    /**
763     * @hide
764     */
765    public final IContentProvider acquireProvider(String name) {
766        if(name == null) {
767            return null;
768        }
769        return acquireProvider(mContext, name);
770    }
771
772    /**
773     * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
774     * that services the content at uri, starting the provider if necessary. Returns
775     * null if there is no provider associated wih the uri. The caller must indicate that they are
776     * done with the provider by calling {@link ContentProviderClient#release} which will allow
777     * the system to release the provider it it determines that there is no other reason for
778     * keeping it active.
779     * @param uri specifies which provider should be acquired
780     * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
781     * that services the content at uri or null if there isn't one.
782     */
783    public final ContentProviderClient acquireContentProviderClient(Uri uri) {
784        IContentProvider provider = acquireProvider(uri);
785        if (provider != null) {
786            return new ContentProviderClient(this, provider);
787        }
788
789        return null;
790    }
791
792    /**
793     * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
794     * with the authority of name, starting the provider if necessary. Returns
795     * null if there is no provider associated wih the uri. The caller must indicate that they are
796     * done with the provider by calling {@link ContentProviderClient#release} which will allow
797     * the system to release the provider it it determines that there is no other reason for
798     * keeping it active.
799     * @param name specifies which provider should be acquired
800     * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
801     * with the authority of name or null if there isn't one.
802     */
803    public final ContentProviderClient acquireContentProviderClient(String name) {
804        IContentProvider provider = acquireProvider(name);
805        if (provider != null) {
806            return new ContentProviderClient(this, provider);
807        }
808
809        return null;
810    }
811
812    /**
813     * Register an observer class that gets callbacks when data identified by a
814     * given content URI changes.
815     *
816     * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI
817     * for a whole class of content.
818     * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code>
819     * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI
820     * specified by <em>uri</em> will cause notifications to be sent. If true, than any URI values
821     * at or below the specified URI will also trigger a match.
822     * @param observer The object that receives callbacks when changes occur.
823     * @see #unregisterContentObserver
824     */
825    public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
826            ContentObserver observer)
827    {
828        try {
829            getContentService().registerContentObserver(uri, notifyForDescendents,
830                    observer.getContentObserver());
831        } catch (RemoteException e) {
832        }
833    }
834
835    /**
836     * Unregisters a change observer.
837     *
838     * @param observer The previously registered observer that is no longer needed.
839     * @see #registerContentObserver
840     */
841    public final void unregisterContentObserver(ContentObserver observer) {
842        try {
843            IContentObserver contentObserver = observer.releaseContentObserver();
844            if (contentObserver != null) {
845                getContentService().unregisterContentObserver(
846                        contentObserver);
847            }
848        } catch (RemoteException e) {
849        }
850    }
851
852    /**
853     * Notify registered observers that a row was updated.
854     * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
855     * By default, CursorAdapter objects will get this notification.
856     *
857     * @param uri
858     * @param observer The observer that originated the change, may be <code>null</null>
859     */
860    public void notifyChange(Uri uri, ContentObserver observer) {
861        notifyChange(uri, observer, true /* sync to network */);
862    }
863
864    /**
865     * Notify registered observers that a row was updated.
866     * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
867     * By default, CursorAdapter objects will get this notification.
868     *
869     * @param uri
870     * @param observer The observer that originated the change, may be <code>null</null>
871     * @param syncToNetwork If true, attempt to sync the change to the network.
872     */
873    public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
874        try {
875            getContentService().notifyChange(
876                    uri, observer == null ? null : observer.getContentObserver(),
877                    observer != null && observer.deliverSelfNotifications(), syncToNetwork);
878        } catch (RemoteException e) {
879        }
880    }
881
882    /**
883     * Start an asynchronous sync operation. If you want to monitor the progress
884     * of the sync you may register a SyncObserver. Only values of the following
885     * types may be used in the extras bundle:
886     * <ul>
887     * <li>Integer</li>
888     * <li>Long</li>
889     * <li>Boolean</li>
890     * <li>Float</li>
891     * <li>Double</li>
892     * <li>String</li>
893     * </ul>
894     *
895     * @param uri the uri of the provider to sync or null to sync all providers.
896     * @param extras any extras to pass to the SyncAdapter.
897     * @deprecated instead use
898     * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
899     */
900    @Deprecated
901    public void startSync(Uri uri, Bundle extras) {
902        Account account = null;
903        if (extras != null) {
904            String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT);
905            if (!TextUtils.isEmpty(accountName)) {
906                account = new Account(accountName, "com.google.GAIA");
907            }
908            extras.remove(SYNC_EXTRAS_ACCOUNT);
909        }
910        requestSync(account, uri != null ? uri.getAuthority() : null, extras);
911    }
912
913    /**
914     * Start an asynchronous sync operation. If you want to monitor the progress
915     * of the sync you may register a SyncObserver. Only values of the following
916     * types may be used in the extras bundle:
917     * <ul>
918     * <li>Integer</li>
919     * <li>Long</li>
920     * <li>Boolean</li>
921     * <li>Float</li>
922     * <li>Double</li>
923     * <li>String</li>
924     * </ul>
925     *
926     * @param account which account should be synced
927     * @param authority which authority should be synced
928     * @param extras any extras to pass to the SyncAdapter.
929     */
930    public static void requestSync(Account account, String authority, Bundle extras) {
931        validateSyncExtrasBundle(extras);
932        try {
933            getContentService().requestSync(account, authority, extras);
934        } catch (RemoteException e) {
935        }
936    }
937
938    /**
939     * Check that only values of the following types are in the Bundle:
940     * <ul>
941     * <li>Integer</li>
942     * <li>Long</li>
943     * <li>Boolean</li>
944     * <li>Float</li>
945     * <li>Double</li>
946     * <li>String</li>
947     * <li>Account</li>
948     * <li>null</li>
949     * </ul>
950     * @param extras the Bundle to check
951     */
952    public static void validateSyncExtrasBundle(Bundle extras) {
953        try {
954            for (String key : extras.keySet()) {
955                Object value = extras.get(key);
956                if (value == null) continue;
957                if (value instanceof Long) continue;
958                if (value instanceof Integer) continue;
959                if (value instanceof Boolean) continue;
960                if (value instanceof Float) continue;
961                if (value instanceof Double) continue;
962                if (value instanceof String) continue;
963                if (value instanceof Account) continue;
964                throw new IllegalArgumentException("unexpected value type: "
965                        + value.getClass().getName());
966            }
967        } catch (IllegalArgumentException e) {
968            throw e;
969        } catch (RuntimeException exc) {
970            throw new IllegalArgumentException("error unparceling Bundle", exc);
971        }
972    }
973
974    /**
975     * Cancel any active or pending syncs that match the Uri. If the uri is null then
976     * all syncs will be canceled.
977     *
978     * @param uri the uri of the provider to sync or null to sync all providers.
979     * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)}
980     */
981    @Deprecated
982    public void cancelSync(Uri uri) {
983        cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null);
984    }
985
986    /**
987     * Cancel any active or pending syncs that match account and authority. The account and
988     * authority can each independently be set to null, which means that syncs with any account
989     * or authority, respectively, will match.
990     *
991     * @param account filters the syncs that match by this account
992     * @param authority filters the syncs that match by this authority
993     */
994    public static void cancelSync(Account account, String authority) {
995        try {
996            getContentService().cancelSync(account, authority);
997        } catch (RemoteException e) {
998        }
999    }
1000
1001    /**
1002     * Get information about the SyncAdapters that are known to the system.
1003     * @return an array of SyncAdapters that have registered with the system
1004     */
1005    public static SyncAdapterType[] getSyncAdapterTypes() {
1006        try {
1007            return getContentService().getSyncAdapterTypes();
1008        } catch (RemoteException e) {
1009            throw new RuntimeException("the ContentService should always be reachable", e);
1010        }
1011    }
1012
1013    /**
1014     * Check if the provider should be synced when a network tickle is received
1015     *
1016     * @param account the account whose setting we are querying
1017     * @param authority the provider whose setting we are querying
1018     * @return true if the provider should be synced when a network tickle is received
1019     */
1020    public static boolean getSyncAutomatically(Account account, String authority) {
1021        try {
1022            return getContentService().getSyncAutomatically(account, authority);
1023        } catch (RemoteException e) {
1024            throw new RuntimeException("the ContentService should always be reachable", e);
1025        }
1026    }
1027
1028    /**
1029     * Set whether or not the provider is synced when it receives a network tickle.
1030     *
1031     * @param account the account whose setting we are querying
1032     * @param authority the provider whose behavior is being controlled
1033     * @param sync true if the provider should be synced when tickles are received for it
1034     */
1035    public static void setSyncAutomatically(Account account, String authority, boolean sync) {
1036        try {
1037            getContentService().setSyncAutomatically(account, authority, sync);
1038        } catch (RemoteException e) {
1039            // exception ignored; if this is thrown then it means the runtime is in the midst of
1040            // being restarted
1041        }
1042    }
1043
1044    /**
1045     * Check if this account/provider is syncable.
1046     * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
1047     */
1048    public static int getIsSyncable(Account account, String authority) {
1049        try {
1050            return getContentService().getIsSyncable(account, authority);
1051        } catch (RemoteException e) {
1052            throw new RuntimeException("the ContentService should always be reachable", e);
1053        }
1054    }
1055
1056    /**
1057     * Set whether this account/provider is syncable.
1058     * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown
1059     */
1060    public static void setIsSyncable(Account account, String authority, int syncable) {
1061        try {
1062            getContentService().setIsSyncable(account, authority, syncable);
1063        } catch (RemoteException e) {
1064            // exception ignored; if this is thrown then it means the runtime is in the midst of
1065            // being restarted
1066        }
1067    }
1068
1069    /**
1070     * Gets the master auto-sync setting that applies to all the providers and accounts.
1071     * If this is false then the per-provider auto-sync setting is ignored.
1072     *
1073     * @return the master auto-sync setting that applies to all the providers and accounts
1074     */
1075    public static boolean getMasterSyncAutomatically() {
1076        try {
1077            return getContentService().getMasterSyncAutomatically();
1078        } catch (RemoteException e) {
1079            throw new RuntimeException("the ContentService should always be reachable", e);
1080        }
1081    }
1082
1083    /**
1084     * Sets the master auto-sync setting that applies to all the providers and accounts.
1085     * If this is false then the per-provider auto-sync setting is ignored.
1086     *
1087     * @param sync the master auto-sync setting that applies to all the providers and accounts
1088     */
1089    public static void setMasterSyncAutomatically(boolean sync) {
1090        try {
1091            getContentService().setMasterSyncAutomatically(sync);
1092        } catch (RemoteException e) {
1093            // exception ignored; if this is thrown then it means the runtime is in the midst of
1094            // being restarted
1095        }
1096    }
1097
1098    /**
1099     * Returns true if there is currently a sync operation for the given
1100     * account or authority in the pending list, or actively being processed.
1101     * @param account the account whose setting we are querying
1102     * @param authority the provider whose behavior is being queried
1103     * @return true if a sync is active for the given account or authority.
1104     */
1105    public static boolean isSyncActive(Account account, String authority) {
1106        try {
1107            return getContentService().isSyncActive(account, authority);
1108        } catch (RemoteException e) {
1109            throw new RuntimeException("the ContentService should always be reachable", e);
1110        }
1111    }
1112
1113    /**
1114     * If a sync is active returns the information about it, otherwise returns false.
1115     * @return the ActiveSyncInfo for the currently active sync or null if one is not active.
1116     * @hide
1117     */
1118    public static ActiveSyncInfo getActiveSync() {
1119        try {
1120            return getContentService().getActiveSync();
1121        } catch (RemoteException e) {
1122            throw new RuntimeException("the ContentService should always be reachable", e);
1123        }
1124    }
1125
1126    /**
1127     * Returns the status that matches the authority.
1128     * @param account the account whose setting we are querying
1129     * @param authority the provider whose behavior is being queried
1130     * @return the SyncStatusInfo for the authority, or null if none exists
1131     * @hide
1132     */
1133    public static SyncStatusInfo getSyncStatus(Account account, String authority) {
1134        try {
1135            return getContentService().getSyncStatus(account, authority);
1136        } catch (RemoteException e) {
1137            throw new RuntimeException("the ContentService should always be reachable", e);
1138        }
1139    }
1140
1141    /**
1142     * Return true if the pending status is true of any matching authorities.
1143     * @param account the account whose setting we are querying
1144     * @param authority the provider whose behavior is being queried
1145     * @return true if there is a pending sync with the matching account and authority
1146     */
1147    public static boolean isSyncPending(Account account, String authority) {
1148        try {
1149            return getContentService().isSyncPending(account, authority);
1150        } catch (RemoteException e) {
1151            throw new RuntimeException("the ContentService should always be reachable", e);
1152        }
1153    }
1154
1155    public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) {
1156        try {
1157            ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() {
1158                public void onStatusChanged(int which) throws RemoteException {
1159                    callback.onStatusChanged(which);
1160                }
1161            };
1162            getContentService().addStatusChangeListener(mask, observer);
1163            return observer;
1164        } catch (RemoteException e) {
1165            throw new RuntimeException("the ContentService should always be reachable", e);
1166        }
1167    }
1168
1169    public static void removeStatusChangeListener(Object handle) {
1170        try {
1171            getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle);
1172        } catch (RemoteException e) {
1173            // exception ignored; if this is thrown then it means the runtime is in the midst of
1174            // being restarted
1175        }
1176    }
1177
1178
1179    private final class CursorWrapperInner extends CursorWrapper {
1180        private IContentProvider mContentProvider;
1181        public static final String TAG="CursorWrapperInner";
1182        private boolean mCloseFlag = false;
1183
1184        CursorWrapperInner(Cursor cursor, IContentProvider icp) {
1185            super(cursor);
1186            mContentProvider = icp;
1187        }
1188
1189        @Override
1190        public void close() {
1191            super.close();
1192            ContentResolver.this.releaseProvider(mContentProvider);
1193            mCloseFlag = true;
1194        }
1195
1196        @Override
1197        protected void finalize() throws Throwable {
1198            try {
1199                if(!mCloseFlag) {
1200                    ContentResolver.this.releaseProvider(mContentProvider);
1201                }
1202            } finally {
1203                super.finalize();
1204            }
1205        }
1206    }
1207
1208    private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
1209        private IContentProvider mContentProvider;
1210        public static final String TAG="ParcelFileDescriptorInner";
1211        private boolean mReleaseProviderFlag = false;
1212
1213        ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
1214            super(pfd);
1215            mContentProvider = icp;
1216        }
1217
1218        @Override
1219        public void close() throws IOException {
1220            if(!mReleaseProviderFlag) {
1221                super.close();
1222                ContentResolver.this.releaseProvider(mContentProvider);
1223                mReleaseProviderFlag = true;
1224            }
1225        }
1226
1227        @Override
1228        protected void finalize() throws Throwable {
1229            if (!mReleaseProviderFlag) {
1230                close();
1231            }
1232        }
1233    }
1234
1235    /** @hide */
1236    public static final String CONTENT_SERVICE_NAME = "content";
1237
1238    /** @hide */
1239    public static IContentService getContentService() {
1240        if (sContentService != null) {
1241            return sContentService;
1242        }
1243        IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
1244        if (Config.LOGV) Log.v("ContentService", "default service binder = " + b);
1245        sContentService = IContentService.Stub.asInterface(b);
1246        if (Config.LOGV) Log.v("ContentService", "default service = " + sContentService);
1247        return sContentService;
1248    }
1249
1250    private static IContentService sContentService;
1251    private final Context mContext;
1252    private static final String TAG = "ContentResolver";
1253}
1254