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