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