ContentResolver.java revision baaf8c3f55d9d95b9d2cd9279e2ce1725da0dc52
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 dalvik.system.CloseGuard;
20
21import android.accounts.Account;
22import android.app.ActivityManagerNative;
23import android.app.ActivityThread;
24import android.app.AppGlobals;
25import android.content.pm.PackageManager.NameNotFoundException;
26import android.content.res.AssetFileDescriptor;
27import android.content.res.Resources;
28import android.database.ContentObserver;
29import android.database.Cursor;
30import android.database.CursorWrapper;
31import android.database.IContentObserver;
32import android.net.Uri;
33import android.os.Bundle;
34import android.os.IBinder;
35import android.os.ParcelFileDescriptor;
36import android.os.RemoteException;
37import android.os.ServiceManager;
38import android.os.StrictMode;
39import android.os.SystemClock;
40import android.text.TextUtils;
41import android.util.EventLog;
42import android.util.Log;
43
44import java.io.File;
45import java.io.FileInputStream;
46import java.io.FileNotFoundException;
47import java.io.IOException;
48import java.io.InputStream;
49import java.io.OutputStream;
50import java.util.ArrayList;
51import java.util.List;
52import java.util.Random;
53
54
55/**
56 * This class provides applications access to the content model.
57 */
58public abstract class ContentResolver {
59    /**
60     * @deprecated instead use
61     * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
62     */
63    @Deprecated
64    public static final String SYNC_EXTRAS_ACCOUNT = "account";
65    public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
66    /**
67     * @deprecated instead use
68     * {@link #SYNC_EXTRAS_MANUAL}
69     */
70    @Deprecated
71    public static final String SYNC_EXTRAS_FORCE = "force";
72
73    /**
74     * If this extra is set to true then the sync settings (like getSyncAutomatically())
75     * are ignored by the sync scheduler.
76     */
77    public static final String SYNC_EXTRAS_IGNORE_SETTINGS = "ignore_settings";
78
79    /**
80     * If this extra is set to true then any backoffs for the initial attempt (e.g. due to retries)
81     * are ignored by the sync scheduler. If this request fails and gets rescheduled then the
82     * retries will still honor the backoff.
83     */
84    public static final String SYNC_EXTRAS_IGNORE_BACKOFF = "ignore_backoff";
85
86    /**
87     * If this extra is set to true then the request will not be retried if it fails.
88     */
89    public static final String SYNC_EXTRAS_DO_NOT_RETRY = "do_not_retry";
90
91    /**
92     * Setting this extra is the equivalent of setting both {@link #SYNC_EXTRAS_IGNORE_SETTINGS}
93     * and {@link #SYNC_EXTRAS_IGNORE_BACKOFF}
94     */
95    public static final String SYNC_EXTRAS_MANUAL = "force";
96
97    public static final String SYNC_EXTRAS_UPLOAD = "upload";
98    public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
99    public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
100
101    /**
102     * Set by the SyncManager to request that the SyncAdapter initialize itself for
103     * the given account/authority pair. One required initialization step is to
104     * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
105     * called with a >= 0 value. When this flag is set the SyncAdapter does not need to
106     * do a full sync, though it is allowed to do so.
107     */
108    public static final String SYNC_EXTRAS_INITIALIZE = "initialize";
109
110    public static final String SCHEME_CONTENT = "content";
111    public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
112    public static final String SCHEME_FILE = "file";
113
114    /**
115     * This is the Android platform's base MIME type for a content: URI
116     * containing a Cursor of a single item.  Applications should use this
117     * as the base type along with their own sub-type of their content: URIs
118     * that represent a particular item.  For example, hypothetical IMAP email
119     * client may have a URI
120     * <code>content://com.company.provider.imap/inbox/1</code> for a particular
121     * message in the inbox, whose MIME type would be reported as
122     * <code>CURSOR_ITEM_BASE_TYPE + "/vnd.company.imap-msg"</code>
123     *
124     * <p>Compare with {@link #CURSOR_DIR_BASE_TYPE}.
125     */
126    public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
127
128    /**
129     * This is the Android platform's base MIME type for a content: URI
130     * containing a Cursor of zero or more items.  Applications should use this
131     * as the base type along with their own sub-type of their content: URIs
132     * that represent a directory of items.  For example, hypothetical IMAP email
133     * client may have a URI
134     * <code>content://com.company.provider.imap/inbox</code> for all of the
135     * messages in its inbox, whose MIME type would be reported as
136     * <code>CURSOR_DIR_BASE_TYPE + "/vnd.company.imap-msg"</code>
137     *
138     * <p>Note how the base MIME type varies between this and
139     * {@link #CURSOR_ITEM_BASE_TYPE} depending on whether there is
140     * one single item or multiple items in the data set, while the sub-type
141     * remains the same because in either case the data structure contained
142     * in the cursor is the same.
143     */
144    public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
145
146    /** @hide */
147    public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
148    /** @hide */
149    public static final int SYNC_ERROR_AUTHENTICATION = 2;
150    /** @hide */
151    public static final int SYNC_ERROR_IO = 3;
152    /** @hide */
153    public static final int SYNC_ERROR_PARSE = 4;
154    /** @hide */
155    public static final int SYNC_ERROR_CONFLICT = 5;
156    /** @hide */
157    public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6;
158    /** @hide */
159    public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7;
160    /** @hide */
161    public static final int SYNC_ERROR_INTERNAL = 8;
162
163    public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0;
164    public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1;
165    public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2;
166    /** @hide */
167    public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3;
168    /** @hide */
169    public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
170
171    // Always log queries which take 500ms+; shorter queries are
172    // sampled accordingly.
173    private static final int SLOW_THRESHOLD_MILLIS = 500;
174    private final Random mRandom = new Random();  // guarded by itself
175
176    public ContentResolver(Context context) {
177        mContext = context;
178    }
179
180    /** @hide */
181    protected abstract IContentProvider acquireProvider(Context c, String name);
182    /** Providing a default implementation of this, to avoid having to change
183     * a lot of other things, but implementations of ContentResolver should
184     * implement it. @hide */
185    protected IContentProvider acquireExistingProvider(Context c, String name) {
186        return acquireProvider(c, name);
187    }
188    /** @hide */
189    public abstract boolean releaseProvider(IContentProvider icp);
190
191    /**
192     * Return the MIME type of the given content URL.
193     *
194     * @param url A Uri identifying content (either a list or specific type),
195     * using the content:// scheme.
196     * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
197     */
198    public final String getType(Uri url) {
199        IContentProvider provider = acquireExistingProvider(url);
200        if (provider != null) {
201            try {
202                return provider.getType(url);
203            } catch (RemoteException e) {
204                return null;
205            } catch (java.lang.Exception e) {
206                Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
207                return null;
208            } finally {
209                releaseProvider(provider);
210            }
211        }
212
213        if (!SCHEME_CONTENT.equals(url.getScheme())) {
214            return null;
215        }
216
217        try {
218            String type = ActivityManagerNative.getDefault().getProviderMimeType(url);
219            return type;
220        } catch (RemoteException e) {
221            // Arbitrary and not worth documenting, as Activity
222            // Manager will kill this process shortly anyway.
223            return null;
224        } catch (java.lang.Exception e) {
225            Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
226            return null;
227        }
228    }
229
230    /**
231     * Query for the possible MIME types for the representations the given
232     * content URL can be returned when opened as as stream with
233     * {@link #openTypedAssetFileDescriptor}.  Note that the types here are
234     * not necessarily a superset of the type returned by {@link #getType} --
235     * many content providers can not return a raw stream for the structured
236     * data that they contain.
237     *
238     * @param url A Uri identifying content (either a list or specific type),
239     * using the content:// scheme.
240     * @param mimeTypeFilter The desired MIME type.  This may be a pattern,
241     * such as *\/*, to query for all available MIME types that match the
242     * pattern.
243     * @return Returns an array of MIME type strings for all availablle
244     * data streams that match the given mimeTypeFilter.  If there are none,
245     * null is returned.
246     */
247    public String[] getStreamTypes(Uri url, String mimeTypeFilter) {
248        IContentProvider provider = acquireProvider(url);
249        if (provider == null) {
250            return null;
251        }
252
253        try {
254            return provider.getStreamTypes(url, mimeTypeFilter);
255        } catch (RemoteException e) {
256            // Arbitrary and not worth documenting, as Activity
257            // Manager will kill this process shortly anyway.
258            return null;
259        } finally {
260            releaseProvider(provider);
261        }
262    }
263
264    /**
265     * <p>
266     * Query the given URI, returning a {@link Cursor} over the result set.
267     * </p>
268     * <p>
269     * For best performance, the caller should follow these guidelines:
270     * <ul>
271     * <li>Provide an explicit projection, to prevent
272     * reading data from storage that aren't going to be used.</li>
273     * <li>Use question mark parameter markers such as 'phone=?' instead of
274     * explicit values in the {@code selection} parameter, so that queries
275     * that differ only by those values will be recognized as the same
276     * for caching purposes.</li>
277     * </ul>
278     * </p>
279     *
280     * @param uri The URI, using the content:// scheme, for the content to
281     *         retrieve.
282     * @param projection A list of which columns to return. Passing null will
283     *         return all columns, which is inefficient.
284     * @param selection A filter declaring which rows to return, formatted as an
285     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
286     *         return all rows for the given URI.
287     * @param selectionArgs You may include ?s in selection, which will be
288     *         replaced by the values from selectionArgs, in the order that they
289     *         appear in the selection. The values will be bound as Strings.
290     * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
291     *         clause (excluding the ORDER BY itself). Passing null will use the
292     *         default sort order, which may be unordered.
293     * @return A Cursor object, which is positioned before the first entry, or null
294     * @see Cursor
295     */
296    public final Cursor query(Uri uri, String[] projection,
297            String selection, String[] selectionArgs, String sortOrder) {
298        IContentProvider provider = acquireProvider(uri);
299        if (provider == null) {
300            return null;
301        }
302        try {
303            long startTime = SystemClock.uptimeMillis();
304            Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder);
305            if (qCursor == null) {
306                releaseProvider(provider);
307                return null;
308            }
309            // force query execution
310            qCursor.getCount();
311            long durationMillis = SystemClock.uptimeMillis() - startTime;
312            maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
313            // Wrap the cursor object into CursorWrapperInner object
314            return new CursorWrapperInner(qCursor, provider);
315        } catch (RemoteException e) {
316            releaseProvider(provider);
317
318            // Arbitrary and not worth documenting, as Activity
319            // Manager will kill this process shortly anyway.
320            return null;
321        } catch (RuntimeException e) {
322            releaseProvider(provider);
323            throw e;
324        }
325    }
326
327    /**
328     * Open a stream on to the content associated with a content URI.  If there
329     * is no data associated with the URI, FileNotFoundException is thrown.
330     *
331     * <h5>Accepts the following URI schemes:</h5>
332     * <ul>
333     * <li>content ({@link #SCHEME_CONTENT})</li>
334     * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
335     * <li>file ({@link #SCHEME_FILE})</li>
336     * </ul>
337     *
338     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
339     * on these schemes.
340     *
341     * @param uri The desired URI.
342     * @return InputStream
343     * @throws FileNotFoundException if the provided URI could not be opened.
344     * @see #openAssetFileDescriptor(Uri, String)
345     */
346    public final InputStream openInputStream(Uri uri)
347            throws FileNotFoundException {
348        String scheme = uri.getScheme();
349        if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
350            // Note: left here to avoid breaking compatibility.  May be removed
351            // with sufficient testing.
352            OpenResourceIdResult r = getResourceId(uri);
353            try {
354                InputStream stream = r.r.openRawResource(r.id);
355                return stream;
356            } catch (Resources.NotFoundException ex) {
357                throw new FileNotFoundException("Resource does not exist: " + uri);
358            }
359        } else if (SCHEME_FILE.equals(scheme)) {
360            // Note: left here to avoid breaking compatibility.  May be removed
361            // with sufficient testing.
362            return new FileInputStream(uri.getPath());
363        } else {
364            AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r");
365            try {
366                return fd != null ? fd.createInputStream() : null;
367            } catch (IOException e) {
368                throw new FileNotFoundException("Unable to create stream");
369            }
370        }
371    }
372
373    /**
374     * Synonym for {@link #openOutputStream(Uri, String)
375     * openOutputStream(uri, "w")}.
376     * @throws FileNotFoundException if the provided URI could not be opened.
377     */
378    public final OutputStream openOutputStream(Uri uri)
379            throws FileNotFoundException {
380        return openOutputStream(uri, "w");
381    }
382
383    /**
384     * Open a stream on to the content associated with a content URI.  If there
385     * is no data associated with the URI, FileNotFoundException is thrown.
386     *
387     * <h5>Accepts the following URI schemes:</h5>
388     * <ul>
389     * <li>content ({@link #SCHEME_CONTENT})</li>
390     * <li>file ({@link #SCHEME_FILE})</li>
391     * </ul>
392     *
393     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
394     * on these schemes.
395     *
396     * @param uri The desired URI.
397     * @param mode May be "w", "wa", "rw", or "rwt".
398     * @return OutputStream
399     * @throws FileNotFoundException if the provided URI could not be opened.
400     * @see #openAssetFileDescriptor(Uri, String)
401     */
402    public final OutputStream openOutputStream(Uri uri, String mode)
403            throws FileNotFoundException {
404        AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode);
405        try {
406            return fd != null ? fd.createOutputStream() : null;
407        } catch (IOException e) {
408            throw new FileNotFoundException("Unable to create stream");
409        }
410    }
411
412    /**
413     * Open a raw file descriptor to access data under a URI.  This
414     * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
415     * underlying {@link ContentProvider#openFile}
416     * ContentProvider.openFile()} method, so will <em>not</em> work with
417     * providers that return sub-sections of files.  If at all possible,
418     * you should use {@link #openAssetFileDescriptor(Uri, String)}.  You
419     * will receive a FileNotFoundException exception if the provider returns a
420     * sub-section of a file.
421     *
422     * <h5>Accepts the following URI schemes:</h5>
423     * <ul>
424     * <li>content ({@link #SCHEME_CONTENT})</li>
425     * <li>file ({@link #SCHEME_FILE})</li>
426     * </ul>
427     *
428     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
429     * on these schemes.
430     *
431     * @param uri The desired URI to open.
432     * @param mode The file mode to use, as per {@link ContentProvider#openFile
433     * ContentProvider.openFile}.
434     * @return Returns a new ParcelFileDescriptor pointing to the file.  You
435     * own this descriptor and are responsible for closing it when done.
436     * @throws FileNotFoundException Throws FileNotFoundException of no
437     * file exists under the URI or the mode is invalid.
438     * @see #openAssetFileDescriptor(Uri, String)
439     */
440    public final ParcelFileDescriptor openFileDescriptor(Uri uri,
441            String mode) throws FileNotFoundException {
442        AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode);
443        if (afd == null) {
444            return null;
445        }
446
447        if (afd.getDeclaredLength() < 0) {
448            // This is a full file!
449            return afd.getParcelFileDescriptor();
450        }
451
452        // Client can't handle a sub-section of a file, so close what
453        // we got and bail with an exception.
454        try {
455            afd.close();
456        } catch (IOException e) {
457        }
458
459        throw new FileNotFoundException("Not a whole file");
460    }
461
462    /**
463     * Open a raw file descriptor to access data under a URI.  This
464     * interacts with the underlying {@link ContentProvider#openAssetFile}
465     * method of the provider associated with the given URI, to retrieve any file stored there.
466     *
467     * <h5>Accepts the following URI schemes:</h5>
468     * <ul>
469     * <li>content ({@link #SCHEME_CONTENT})</li>
470     * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
471     * <li>file ({@link #SCHEME_FILE})</li>
472     * </ul>
473     * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
474     * <p>
475     * A Uri object can be used to reference a resource in an APK file.  The
476     * Uri should be one of the following formats:
477     * <ul>
478     * <li><code>android.resource://package_name/id_number</code><br/>
479     * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
480     * For example <code>com.example.myapp</code><br/>
481     * <code>id_number</code> is the int form of the ID.<br/>
482     * The easiest way to construct this form is
483     * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
484     * </li>
485     * <li><code>android.resource://package_name/type/name</code><br/>
486     * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
487     * For example <code>com.example.myapp</code><br/>
488     * <code>type</code> is the string form of the resource type.  For example, <code>raw</code>
489     * or <code>drawable</code>.
490     * <code>name</code> is the string form of the resource name.  That is, whatever the file
491     * name was in your res directory, without the type extension.
492     * The easiest way to construct this form is
493     * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
494     * </li>
495     * </ul>
496     *
497     * <p>Note that if this function is called for read-only input (mode is "r")
498     * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
499     * for you with a MIME type of "*\/*".  This allows such callers to benefit
500     * from any built-in data conversion that a provider implements.
501     *
502     * @param uri The desired URI to open.
503     * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
504     * ContentProvider.openAssetFile}.
505     * @return Returns a new ParcelFileDescriptor pointing to the file.  You
506     * own this descriptor and are responsible for closing it when done.
507     * @throws FileNotFoundException Throws FileNotFoundException of no
508     * file exists under the URI or the mode is invalid.
509     */
510    public final AssetFileDescriptor openAssetFileDescriptor(Uri uri,
511            String mode) throws FileNotFoundException {
512        String scheme = uri.getScheme();
513        if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
514            if (!"r".equals(mode)) {
515                throw new FileNotFoundException("Can't write resources: " + uri);
516            }
517            OpenResourceIdResult r = getResourceId(uri);
518            try {
519                return r.r.openRawResourceFd(r.id);
520            } catch (Resources.NotFoundException ex) {
521                throw new FileNotFoundException("Resource does not exist: " + uri);
522            }
523        } else if (SCHEME_FILE.equals(scheme)) {
524            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
525                    new File(uri.getPath()), modeToMode(uri, mode));
526            return new AssetFileDescriptor(pfd, 0, -1);
527        } else {
528            if ("r".equals(mode)) {
529                return openTypedAssetFileDescriptor(uri, "*/*", null);
530            } else {
531                IContentProvider provider = acquireProvider(uri);
532                if (provider == null) {
533                    throw new FileNotFoundException("No content provider: " + uri);
534                }
535                try {
536                    AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
537                    if(fd == null) {
538                        // The provider will be released by the finally{} clause
539                        return null;
540                    }
541                    ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
542                            fd.getParcelFileDescriptor(), provider);
543
544                    // Success!  Don't release the provider when exiting, let
545                    // ParcelFileDescriptorInner do that when it is closed.
546                    provider = null;
547
548                    return new AssetFileDescriptor(pfd, fd.getStartOffset(),
549                            fd.getDeclaredLength());
550                } catch (RemoteException e) {
551                    // Somewhat pointless, as Activity Manager will kill this
552                    // process shortly anyway if the depdendent ContentProvider dies.
553                    throw new FileNotFoundException("Dead content provider: " + uri);
554                } catch (FileNotFoundException e) {
555                    throw e;
556                } finally {
557                    if (provider != null) {
558                        releaseProvider(provider);
559                    }
560                }
561            }
562        }
563    }
564
565    /**
566     * Open a raw file descriptor to access (potentially type transformed)
567     * data from a "content:" URI.  This interacts with the underlying
568     * {@link ContentProvider#openTypedAssetFile} method of the provider
569     * associated with the given URI, to retrieve retrieve any appropriate
570     * data stream for the data stored there.
571     *
572     * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
573     * with "content:" URIs, because content providers are the only facility
574     * with an associated MIME type to ensure that the returned data stream
575     * is of the desired type.
576     *
577     * <p>All text/* streams are encoded in UTF-8.
578     *
579     * @param uri The desired URI to open.
580     * @param mimeType The desired MIME type of the returned data.  This can
581     * be a pattern such as *\/*, which will allow the content provider to
582     * select a type, though there is no way for you to determine what type
583     * it is returning.
584     * @param opts Additional provider-dependent options.
585     * @return Returns a new ParcelFileDescriptor from which you can read the
586     * data stream from the provider.  Note that this may be a pipe, meaning
587     * you can't seek in it.  The only seek you should do is if the
588     * AssetFileDescriptor contains an offset, to move to that offset before
589     * reading.  You own this descriptor and are responsible for closing it when done.
590     * @throws FileNotFoundException Throws FileNotFoundException of no
591     * data of the desired type exists under the URI.
592     */
593    public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
594            String mimeType, Bundle opts) throws FileNotFoundException {
595        IContentProvider provider = acquireProvider(uri);
596        if (provider == null) {
597            throw new FileNotFoundException("No content provider: " + uri);
598        }
599        try {
600            AssetFileDescriptor fd = provider.openTypedAssetFile(uri, mimeType, opts);
601            if (fd == null) {
602                // The provider will be released by the finally{} clause
603                return null;
604            }
605            ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
606                    fd.getParcelFileDescriptor(), provider);
607
608            // Success!  Don't release the provider when exiting, let
609            // ParcelFileDescriptorInner do that when it is closed.
610            provider = null;
611
612            return new AssetFileDescriptor(pfd, fd.getStartOffset(),
613                    fd.getDeclaredLength());
614        } catch (RemoteException e) {
615            throw new FileNotFoundException("Dead content provider: " + uri);
616        } catch (FileNotFoundException e) {
617            throw e;
618        } finally {
619            if (provider != null) {
620                releaseProvider(provider);
621            }
622        }
623    }
624
625    /**
626     * A resource identified by the {@link Resources} that contains it, and a resource id.
627     *
628     * @hide
629     */
630    public class OpenResourceIdResult {
631        public Resources r;
632        public int id;
633    }
634
635    /**
636     * Resolves an android.resource URI to a {@link Resources} and a resource id.
637     *
638     * @hide
639     */
640    public OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException {
641        String authority = uri.getAuthority();
642        Resources r;
643        if (TextUtils.isEmpty(authority)) {
644            throw new FileNotFoundException("No authority: " + uri);
645        } else {
646            try {
647                r = mContext.getPackageManager().getResourcesForApplication(authority);
648            } catch (NameNotFoundException ex) {
649                throw new FileNotFoundException("No package found for authority: " + uri);
650            }
651        }
652        List<String> path = uri.getPathSegments();
653        if (path == null) {
654            throw new FileNotFoundException("No path: " + uri);
655        }
656        int len = path.size();
657        int id;
658        if (len == 1) {
659            try {
660                id = Integer.parseInt(path.get(0));
661            } catch (NumberFormatException e) {
662                throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
663            }
664        } else if (len == 2) {
665            id = r.getIdentifier(path.get(1), path.get(0), authority);
666        } else {
667            throw new FileNotFoundException("More than two path segments: " + uri);
668        }
669        if (id == 0) {
670            throw new FileNotFoundException("No resource found for: " + uri);
671        }
672        OpenResourceIdResult res = new OpenResourceIdResult();
673        res.r = r;
674        res.id = id;
675        return res;
676    }
677
678    /** @hide */
679    static public int modeToMode(Uri uri, String mode) throws FileNotFoundException {
680        int modeBits;
681        if ("r".equals(mode)) {
682            modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
683        } else if ("w".equals(mode) || "wt".equals(mode)) {
684            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
685                    | ParcelFileDescriptor.MODE_CREATE
686                    | ParcelFileDescriptor.MODE_TRUNCATE;
687        } else if ("wa".equals(mode)) {
688            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
689                    | ParcelFileDescriptor.MODE_CREATE
690                    | ParcelFileDescriptor.MODE_APPEND;
691        } else if ("rw".equals(mode)) {
692            modeBits = ParcelFileDescriptor.MODE_READ_WRITE
693                    | ParcelFileDescriptor.MODE_CREATE;
694        } else if ("rwt".equals(mode)) {
695            modeBits = ParcelFileDescriptor.MODE_READ_WRITE
696                    | ParcelFileDescriptor.MODE_CREATE
697                    | ParcelFileDescriptor.MODE_TRUNCATE;
698        } else {
699            throw new FileNotFoundException("Bad mode for " + uri + ": "
700                    + mode);
701        }
702        return modeBits;
703    }
704
705    /**
706     * Inserts a row into a table at the given URL.
707     *
708     * If the content provider supports transactions the insertion will be atomic.
709     *
710     * @param url The URL of the table to insert into.
711     * @param values The initial values for the newly inserted row. The key is the column name for
712     *               the field. Passing an empty ContentValues will create an empty row.
713     * @return the URL of the newly created row.
714     */
715    public final Uri insert(Uri url, ContentValues values)
716    {
717        IContentProvider provider = acquireProvider(url);
718        if (provider == null) {
719            throw new IllegalArgumentException("Unknown URL " + url);
720        }
721        try {
722            long startTime = SystemClock.uptimeMillis();
723            Uri createdRow = provider.insert(url, values);
724            long durationMillis = SystemClock.uptimeMillis() - startTime;
725            maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
726            return createdRow;
727        } catch (RemoteException e) {
728            // Arbitrary and not worth documenting, as Activity
729            // Manager will kill this process shortly anyway.
730            return null;
731        } finally {
732            releaseProvider(provider);
733        }
734    }
735
736    /**
737     * Applies each of the {@link ContentProviderOperation} objects and returns an array
738     * of their results. Passes through OperationApplicationException, which may be thrown
739     * by the call to {@link ContentProviderOperation#apply}.
740     * If all the applications succeed then a {@link ContentProviderResult} array with the
741     * same number of elements as the operations will be returned. It is implementation-specific
742     * how many, if any, operations will have been successfully applied if a call to
743     * apply results in a {@link OperationApplicationException}.
744     * @param authority the authority of the ContentProvider to which this batch should be applied
745     * @param operations the operations to apply
746     * @return the results of the applications
747     * @throws OperationApplicationException thrown if an application fails.
748     * See {@link ContentProviderOperation#apply} for more information.
749     * @throws RemoteException thrown if a RemoteException is encountered while attempting
750     *   to communicate with a remote provider.
751     */
752    public ContentProviderResult[] applyBatch(String authority,
753            ArrayList<ContentProviderOperation> operations)
754            throws RemoteException, OperationApplicationException {
755        ContentProviderClient provider = acquireContentProviderClient(authority);
756        if (provider == null) {
757            throw new IllegalArgumentException("Unknown authority " + authority);
758        }
759        try {
760            return provider.applyBatch(operations);
761        } finally {
762            provider.release();
763        }
764    }
765
766    /**
767     * Inserts multiple rows into a table at the given URL.
768     *
769     * This function make no guarantees about the atomicity of the insertions.
770     *
771     * @param url The URL of the table to insert into.
772     * @param values The initial values for the newly inserted rows. The key is the column name for
773     *               the field. Passing null will create an empty row.
774     * @return the number of newly created rows.
775     */
776    public final int bulkInsert(Uri url, ContentValues[] values)
777    {
778        IContentProvider provider = acquireProvider(url);
779        if (provider == null) {
780            throw new IllegalArgumentException("Unknown URL " + url);
781        }
782        try {
783            long startTime = SystemClock.uptimeMillis();
784            int rowsCreated = provider.bulkInsert(url, values);
785            long durationMillis = SystemClock.uptimeMillis() - startTime;
786            maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
787            return rowsCreated;
788        } catch (RemoteException e) {
789            // Arbitrary and not worth documenting, as Activity
790            // Manager will kill this process shortly anyway.
791            return 0;
792        } finally {
793            releaseProvider(provider);
794        }
795    }
796
797    /**
798     * Deletes row(s) specified by a content URI.
799     *
800     * If the content provider supports transactions, the deletion will be atomic.
801     *
802     * @param url The URL of the row to delete.
803     * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause
804                    (excluding the WHERE itself).
805     * @return The number of rows deleted.
806     */
807    public final int delete(Uri url, String where, String[] selectionArgs)
808    {
809        IContentProvider provider = acquireProvider(url);
810        if (provider == null) {
811            throw new IllegalArgumentException("Unknown URL " + url);
812        }
813        try {
814            long startTime = SystemClock.uptimeMillis();
815            int rowsDeleted = provider.delete(url, where, selectionArgs);
816            long durationMillis = SystemClock.uptimeMillis() - startTime;
817            maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
818            return rowsDeleted;
819        } catch (RemoteException e) {
820            // Arbitrary and not worth documenting, as Activity
821            // Manager will kill this process shortly anyway.
822            return -1;
823        } finally {
824            releaseProvider(provider);
825        }
826    }
827
828    /**
829     * Update row(s) in a content URI.
830     *
831     * If the content provider supports transactions the update will be atomic.
832     *
833     * @param uri The URI to modify.
834     * @param values The new field values. The key is the column name for the field.
835                     A null value will remove an existing field value.
836     * @param where A filter to apply to rows before updating, formatted as an SQL WHERE clause
837                    (excluding the WHERE itself).
838     * @return the number of rows updated.
839     * @throws NullPointerException if uri or values are null
840     */
841    public final int update(Uri uri, ContentValues values, String where,
842            String[] selectionArgs) {
843        IContentProvider provider = acquireProvider(uri);
844        if (provider == null) {
845            throw new IllegalArgumentException("Unknown URI " + uri);
846        }
847        try {
848            long startTime = SystemClock.uptimeMillis();
849            int rowsUpdated = provider.update(uri, values, where, selectionArgs);
850            long durationMillis = SystemClock.uptimeMillis() - startTime;
851            maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
852            return rowsUpdated;
853        } catch (RemoteException e) {
854            // Arbitrary and not worth documenting, as Activity
855            // Manager will kill this process shortly anyway.
856            return -1;
857        } finally {
858            releaseProvider(provider);
859        }
860    }
861
862    /**
863     * Call an provider-defined method.  This can be used to implement
864     * read or write interfaces which are cheaper than using a Cursor and/or
865     * do not fit into the traditional table model.
866     *
867     * @param method provider-defined method name to call.  Opaque to
868     *   framework, but must be non-null.
869     * @param arg provider-defined String argument.  May be null.
870     * @param extras provider-defined Bundle argument.  May be null.
871     * @return a result Bundle, possibly null.  Will be null if the ContentProvider
872     *   does not implement call.
873     * @throws NullPointerException if uri or method is null
874     * @throws IllegalArgumentException if uri is not known
875     */
876    public final Bundle call(Uri uri, String method, String arg, Bundle extras) {
877        if (uri == null) {
878            throw new NullPointerException("uri == null");
879        }
880        if (method == null) {
881            throw new NullPointerException("method == null");
882        }
883        IContentProvider provider = acquireProvider(uri);
884        if (provider == null) {
885            throw new IllegalArgumentException("Unknown URI " + uri);
886        }
887        try {
888            return provider.call(method, arg, extras);
889        } catch (RemoteException e) {
890            // Arbitrary and not worth documenting, as Activity
891            // Manager will kill this process shortly anyway.
892            return null;
893        } finally {
894            releaseProvider(provider);
895        }
896    }
897
898    /**
899     * Returns the content provider for the given content URI.
900     *
901     * @param uri The URI to a content provider
902     * @return The ContentProvider for the given URI, or null if no content provider is found.
903     * @hide
904     */
905    public final IContentProvider acquireProvider(Uri uri) {
906        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
907            return null;
908        }
909        String auth = uri.getAuthority();
910        if (auth != null) {
911            return acquireProvider(mContext, uri.getAuthority());
912        }
913        return null;
914    }
915
916    /**
917     * Returns the content provider for the given content URI if the process
918     * already has a reference on it.
919     *
920     * @param uri The URI to a content provider
921     * @return The ContentProvider for the given URI, or null if no content provider is found.
922     * @hide
923     */
924    public final IContentProvider acquireExistingProvider(Uri uri) {
925        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
926            return null;
927        }
928        String auth = uri.getAuthority();
929        if (auth != null) {
930            return acquireExistingProvider(mContext, uri.getAuthority());
931        }
932        return null;
933    }
934
935    /**
936     * @hide
937     */
938    public final IContentProvider acquireProvider(String name) {
939        if (name == null) {
940            return null;
941        }
942        return acquireProvider(mContext, name);
943    }
944
945    /**
946     * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
947     * that services the content at uri, starting the provider if necessary. Returns
948     * null if there is no provider associated wih the uri. The caller must indicate that they are
949     * done with the provider by calling {@link ContentProviderClient#release} which will allow
950     * the system to release the provider it it determines that there is no other reason for
951     * keeping it active.
952     * @param uri specifies which provider should be acquired
953     * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
954     * that services the content at uri or null if there isn't one.
955     */
956    public final ContentProviderClient acquireContentProviderClient(Uri uri) {
957        IContentProvider provider = acquireProvider(uri);
958        if (provider != null) {
959            return new ContentProviderClient(this, provider);
960        }
961
962        return null;
963    }
964
965    /**
966     * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
967     * with the authority of name, starting the provider if necessary. Returns
968     * null if there is no provider associated wih the uri. The caller must indicate that they are
969     * done with the provider by calling {@link ContentProviderClient#release} which will allow
970     * the system to release the provider it it determines that there is no other reason for
971     * keeping it active.
972     * @param name specifies which provider should be acquired
973     * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
974     * with the authority of name or null if there isn't one.
975     */
976    public final ContentProviderClient acquireContentProviderClient(String name) {
977        IContentProvider provider = acquireProvider(name);
978        if (provider != null) {
979            return new ContentProviderClient(this, provider);
980        }
981
982        return null;
983    }
984
985    /**
986     * Register an observer class that gets callbacks when data identified by a
987     * given content URI changes.
988     *
989     * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI
990     * for a whole class of content.
991     * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code>
992     * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI
993     * specified by <em>uri</em> will cause notifications to be sent. If true, than any URI values
994     * at or below the specified URI will also trigger a match.
995     * @param observer The object that receives callbacks when changes occur.
996     * @see #unregisterContentObserver
997     */
998    public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
999            ContentObserver observer)
1000    {
1001        try {
1002            getContentService().registerContentObserver(uri, notifyForDescendents,
1003                    observer.getContentObserver());
1004        } catch (RemoteException e) {
1005        }
1006    }
1007
1008    /**
1009     * Unregisters a change observer.
1010     *
1011     * @param observer The previously registered observer that is no longer needed.
1012     * @see #registerContentObserver
1013     */
1014    public final void unregisterContentObserver(ContentObserver observer) {
1015        try {
1016            IContentObserver contentObserver = observer.releaseContentObserver();
1017            if (contentObserver != null) {
1018                getContentService().unregisterContentObserver(
1019                        contentObserver);
1020            }
1021        } catch (RemoteException e) {
1022        }
1023    }
1024
1025    /**
1026     * Notify registered observers that a row was updated.
1027     * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
1028     * By default, CursorAdapter objects will get this notification.
1029     *
1030     * @param uri
1031     * @param observer The observer that originated the change, may be <code>null</null>
1032     */
1033    public void notifyChange(Uri uri, ContentObserver observer) {
1034        notifyChange(uri, observer, true /* sync to network */);
1035    }
1036
1037    /**
1038     * Notify registered observers that a row was updated.
1039     * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
1040     * By default, CursorAdapter objects will get this notification.
1041     *
1042     * @param uri
1043     * @param observer The observer that originated the change, may be <code>null</null>
1044     * @param syncToNetwork If true, attempt to sync the change to the network.
1045     */
1046    public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
1047        try {
1048            getContentService().notifyChange(
1049                    uri, observer == null ? null : observer.getContentObserver(),
1050                    observer != null && observer.deliverSelfNotifications(), syncToNetwork);
1051        } catch (RemoteException e) {
1052        }
1053    }
1054
1055    /**
1056     * Start an asynchronous sync operation. If you want to monitor the progress
1057     * of the sync you may register a SyncObserver. Only values of the following
1058     * types may be used in the extras bundle:
1059     * <ul>
1060     * <li>Integer</li>
1061     * <li>Long</li>
1062     * <li>Boolean</li>
1063     * <li>Float</li>
1064     * <li>Double</li>
1065     * <li>String</li>
1066     * </ul>
1067     *
1068     * @param uri the uri of the provider to sync or null to sync all providers.
1069     * @param extras any extras to pass to the SyncAdapter.
1070     * @deprecated instead use
1071     * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
1072     */
1073    @Deprecated
1074    public void startSync(Uri uri, Bundle extras) {
1075        Account account = null;
1076        if (extras != null) {
1077            String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT);
1078            if (!TextUtils.isEmpty(accountName)) {
1079                account = new Account(accountName, "com.google");
1080            }
1081            extras.remove(SYNC_EXTRAS_ACCOUNT);
1082        }
1083        requestSync(account, uri != null ? uri.getAuthority() : null, extras);
1084    }
1085
1086    /**
1087     * Start an asynchronous sync operation. If you want to monitor the progress
1088     * of the sync you may register a SyncObserver. Only values of the following
1089     * types may be used in the extras bundle:
1090     * <ul>
1091     * <li>Integer</li>
1092     * <li>Long</li>
1093     * <li>Boolean</li>
1094     * <li>Float</li>
1095     * <li>Double</li>
1096     * <li>String</li>
1097     * </ul>
1098     *
1099     * @param account which account should be synced
1100     * @param authority which authority should be synced
1101     * @param extras any extras to pass to the SyncAdapter.
1102     */
1103    public static void requestSync(Account account, String authority, Bundle extras) {
1104        validateSyncExtrasBundle(extras);
1105        try {
1106            getContentService().requestSync(account, authority, extras);
1107        } catch (RemoteException e) {
1108        }
1109    }
1110
1111    /**
1112     * Check that only values of the following types are in the Bundle:
1113     * <ul>
1114     * <li>Integer</li>
1115     * <li>Long</li>
1116     * <li>Boolean</li>
1117     * <li>Float</li>
1118     * <li>Double</li>
1119     * <li>String</li>
1120     * <li>Account</li>
1121     * <li>null</li>
1122     * </ul>
1123     * @param extras the Bundle to check
1124     */
1125    public static void validateSyncExtrasBundle(Bundle extras) {
1126        try {
1127            for (String key : extras.keySet()) {
1128                Object value = extras.get(key);
1129                if (value == null) continue;
1130                if (value instanceof Long) continue;
1131                if (value instanceof Integer) continue;
1132                if (value instanceof Boolean) continue;
1133                if (value instanceof Float) continue;
1134                if (value instanceof Double) continue;
1135                if (value instanceof String) continue;
1136                if (value instanceof Account) continue;
1137                throw new IllegalArgumentException("unexpected value type: "
1138                        + value.getClass().getName());
1139            }
1140        } catch (IllegalArgumentException e) {
1141            throw e;
1142        } catch (RuntimeException exc) {
1143            throw new IllegalArgumentException("error unparceling Bundle", exc);
1144        }
1145    }
1146
1147    /**
1148     * Cancel any active or pending syncs that match the Uri. If the uri is null then
1149     * all syncs will be canceled.
1150     *
1151     * @param uri the uri of the provider to sync or null to sync all providers.
1152     * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)}
1153     */
1154    @Deprecated
1155    public void cancelSync(Uri uri) {
1156        cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null);
1157    }
1158
1159    /**
1160     * Cancel any active or pending syncs that match account and authority. The account and
1161     * authority can each independently be set to null, which means that syncs with any account
1162     * or authority, respectively, will match.
1163     *
1164     * @param account filters the syncs that match by this account
1165     * @param authority filters the syncs that match by this authority
1166     */
1167    public static void cancelSync(Account account, String authority) {
1168        try {
1169            getContentService().cancelSync(account, authority);
1170        } catch (RemoteException e) {
1171        }
1172    }
1173
1174    /**
1175     * Get information about the SyncAdapters that are known to the system.
1176     * @return an array of SyncAdapters that have registered with the system
1177     */
1178    public static SyncAdapterType[] getSyncAdapterTypes() {
1179        try {
1180            return getContentService().getSyncAdapterTypes();
1181        } catch (RemoteException e) {
1182            throw new RuntimeException("the ContentService should always be reachable", e);
1183        }
1184    }
1185
1186    /**
1187     * Check if the provider should be synced when a network tickle is received
1188     *
1189     * @param account the account whose setting we are querying
1190     * @param authority the provider whose setting we are querying
1191     * @return true if the provider should be synced when a network tickle is received
1192     */
1193    public static boolean getSyncAutomatically(Account account, String authority) {
1194        try {
1195            return getContentService().getSyncAutomatically(account, authority);
1196        } catch (RemoteException e) {
1197            throw new RuntimeException("the ContentService should always be reachable", e);
1198        }
1199    }
1200
1201    /**
1202     * Set whether or not the provider is synced when it receives a network tickle.
1203     *
1204     * @param account the account whose setting we are querying
1205     * @param authority the provider whose behavior is being controlled
1206     * @param sync true if the provider should be synced when tickles are received for it
1207     */
1208    public static void setSyncAutomatically(Account account, String authority, boolean sync) {
1209        try {
1210            getContentService().setSyncAutomatically(account, authority, sync);
1211        } catch (RemoteException e) {
1212            // exception ignored; if this is thrown then it means the runtime is in the midst of
1213            // being restarted
1214        }
1215    }
1216
1217    /**
1218     * Specifies that a sync should be requested with the specified the account, authority,
1219     * and extras at the given frequency. If there is already another periodic sync scheduled
1220     * with the account, authority and extras then a new periodic sync won't be added, instead
1221     * the frequency of the previous one will be updated.
1222     * <p>
1223     * These periodic syncs honor the "syncAutomatically" and "masterSyncAutomatically" settings.
1224     * Although these sync are scheduled at the specified frequency, it may take longer for it to
1225     * actually be started if other syncs are ahead of it in the sync operation queue. This means
1226     * that the actual start time may drift.
1227     * <p>
1228     * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY},
1229     * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS},
1230     * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE},
1231     * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true.
1232     * If any are supplied then an {@link IllegalArgumentException} will be thrown.
1233     *
1234     * @param account the account to specify in the sync
1235     * @param authority the provider to specify in the sync request
1236     * @param extras extra parameters to go along with the sync request
1237     * @param pollFrequency how frequently the sync should be performed, in seconds.
1238     * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
1239     * are null.
1240     */
1241    public static void addPeriodicSync(Account account, String authority, Bundle extras,
1242            long pollFrequency) {
1243        validateSyncExtrasBundle(extras);
1244        if (account == null) {
1245            throw new IllegalArgumentException("account must not be null");
1246        }
1247        if (authority == null) {
1248            throw new IllegalArgumentException("authority must not be null");
1249        }
1250        if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false)
1251                || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false)
1252                || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false)
1253                || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false)
1254                || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false)
1255                || extras.getBoolean(SYNC_EXTRAS_FORCE, false)
1256                || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) {
1257            throw new IllegalArgumentException("illegal extras were set");
1258        }
1259        try {
1260            getContentService().addPeriodicSync(account, authority, extras, pollFrequency);
1261        } catch (RemoteException e) {
1262            // exception ignored; if this is thrown then it means the runtime is in the midst of
1263            // being restarted
1264        }
1265    }
1266
1267    /**
1268     * Remove a periodic sync. Has no affect if account, authority and extras don't match
1269     * an existing periodic sync.
1270     *
1271     * @param account the account of the periodic sync to remove
1272     * @param authority the provider of the periodic sync to remove
1273     * @param extras the extras of the periodic sync to remove
1274     */
1275    public static void removePeriodicSync(Account account, String authority, Bundle extras) {
1276        validateSyncExtrasBundle(extras);
1277        if (account == null) {
1278            throw new IllegalArgumentException("account must not be null");
1279        }
1280        if (authority == null) {
1281            throw new IllegalArgumentException("authority must not be null");
1282        }
1283        try {
1284            getContentService().removePeriodicSync(account, authority, extras);
1285        } catch (RemoteException e) {
1286            throw new RuntimeException("the ContentService should always be reachable", e);
1287        }
1288    }
1289
1290    /**
1291     * Get the list of information about the periodic syncs for the given account and authority.
1292     *
1293     * @param account the account whose periodic syncs we are querying
1294     * @param authority the provider whose periodic syncs we are querying
1295     * @return a list of PeriodicSync objects. This list may be empty but will never be null.
1296     */
1297    public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) {
1298        if (account == null) {
1299            throw new IllegalArgumentException("account must not be null");
1300        }
1301        if (authority == null) {
1302            throw new IllegalArgumentException("authority must not be null");
1303        }
1304        try {
1305            return getContentService().getPeriodicSyncs(account, authority);
1306        } catch (RemoteException e) {
1307            throw new RuntimeException("the ContentService should always be reachable", e);
1308        }
1309    }
1310
1311    /**
1312     * Check if this account/provider is syncable.
1313     * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
1314     */
1315    public static int getIsSyncable(Account account, String authority) {
1316        try {
1317            return getContentService().getIsSyncable(account, authority);
1318        } catch (RemoteException e) {
1319            throw new RuntimeException("the ContentService should always be reachable", e);
1320        }
1321    }
1322
1323    /**
1324     * Set whether this account/provider is syncable.
1325     * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown
1326     */
1327    public static void setIsSyncable(Account account, String authority, int syncable) {
1328        try {
1329            getContentService().setIsSyncable(account, authority, syncable);
1330        } catch (RemoteException e) {
1331            // exception ignored; if this is thrown then it means the runtime is in the midst of
1332            // being restarted
1333        }
1334    }
1335
1336    /**
1337     * Gets the master auto-sync setting that applies to all the providers and accounts.
1338     * If this is false then the per-provider auto-sync setting is ignored.
1339     *
1340     * @return the master auto-sync setting that applies to all the providers and accounts
1341     */
1342    public static boolean getMasterSyncAutomatically() {
1343        try {
1344            return getContentService().getMasterSyncAutomatically();
1345        } catch (RemoteException e) {
1346            throw new RuntimeException("the ContentService should always be reachable", e);
1347        }
1348    }
1349
1350    /**
1351     * Sets the master auto-sync setting that applies to all the providers and accounts.
1352     * If this is false then the per-provider auto-sync setting is ignored.
1353     *
1354     * @param sync the master auto-sync setting that applies to all the providers and accounts
1355     */
1356    public static void setMasterSyncAutomatically(boolean sync) {
1357        try {
1358            getContentService().setMasterSyncAutomatically(sync);
1359        } catch (RemoteException e) {
1360            // exception ignored; if this is thrown then it means the runtime is in the midst of
1361            // being restarted
1362        }
1363    }
1364
1365    /**
1366     * Returns true if there is currently a sync operation for the given
1367     * account or authority in the pending list, or actively being processed.
1368     * @param account the account whose setting we are querying
1369     * @param authority the provider whose behavior is being queried
1370     * @return true if a sync is active for the given account or authority.
1371     */
1372    public static boolean isSyncActive(Account account, String authority) {
1373        try {
1374            return getContentService().isSyncActive(account, authority);
1375        } catch (RemoteException e) {
1376            throw new RuntimeException("the ContentService should always be reachable", e);
1377        }
1378    }
1379
1380    /**
1381     * If a sync is active returns the information about it, otherwise returns null.
1382     * <p>
1383     * @return the SyncInfo for the currently active sync or null if one is not active.
1384     * @deprecated
1385     * Since multiple concurrent syncs are now supported you should use
1386     * {@link #getCurrentSyncs()} to get the accurate list of current syncs.
1387     * This method returns the first item from the list of current syncs
1388     * or null if there are none.
1389     */
1390    @Deprecated
1391    public static SyncInfo getCurrentSync() {
1392        try {
1393            final List<SyncInfo> syncs = getContentService().getCurrentSyncs();
1394            if (syncs.isEmpty()) {
1395                return null;
1396            }
1397            return syncs.get(0);
1398        } catch (RemoteException e) {
1399            throw new RuntimeException("the ContentService should always be reachable", e);
1400        }
1401    }
1402
1403    /**
1404     * Returns a list with information about all the active syncs. This list will be empty
1405     * if there are no active syncs.
1406     * @return a List of SyncInfo objects for the currently active syncs.
1407     */
1408    public static List<SyncInfo> getCurrentSyncs() {
1409        try {
1410            return getContentService().getCurrentSyncs();
1411        } catch (RemoteException e) {
1412            throw new RuntimeException("the ContentService should always be reachable", e);
1413        }
1414    }
1415
1416    /**
1417     * Returns the status that matches the authority.
1418     * @param account the account whose setting we are querying
1419     * @param authority the provider whose behavior is being queried
1420     * @return the SyncStatusInfo for the authority, or null if none exists
1421     * @hide
1422     */
1423    public static SyncStatusInfo getSyncStatus(Account account, String authority) {
1424        try {
1425            return getContentService().getSyncStatus(account, authority);
1426        } catch (RemoteException e) {
1427            throw new RuntimeException("the ContentService should always be reachable", e);
1428        }
1429    }
1430
1431    /**
1432     * Return true if the pending status is true of any matching authorities.
1433     * @param account the account whose setting we are querying
1434     * @param authority the provider whose behavior is being queried
1435     * @return true if there is a pending sync with the matching account and authority
1436     */
1437    public static boolean isSyncPending(Account account, String authority) {
1438        try {
1439            return getContentService().isSyncPending(account, authority);
1440        } catch (RemoteException e) {
1441            throw new RuntimeException("the ContentService should always be reachable", e);
1442        }
1443    }
1444
1445    /**
1446     * Request notifications when the different aspects of the SyncManager change. The
1447     * different items that can be requested are:
1448     * <ul>
1449     * <li> {@link #SYNC_OBSERVER_TYPE_PENDING}
1450     * <li> {@link #SYNC_OBSERVER_TYPE_ACTIVE}
1451     * <li> {@link #SYNC_OBSERVER_TYPE_SETTINGS}
1452     * </ul>
1453     * The caller can set one or more of the status types in the mask for any
1454     * given listener registration.
1455     * @param mask the status change types that will cause the callback to be invoked
1456     * @param callback observer to be invoked when the status changes
1457     * @return a handle that can be used to remove the listener at a later time
1458     */
1459    public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) {
1460        if (callback == null) {
1461            throw new IllegalArgumentException("you passed in a null callback");
1462        }
1463        try {
1464            ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() {
1465                public void onStatusChanged(int which) throws RemoteException {
1466                    callback.onStatusChanged(which);
1467                }
1468            };
1469            getContentService().addStatusChangeListener(mask, observer);
1470            return observer;
1471        } catch (RemoteException e) {
1472            throw new RuntimeException("the ContentService should always be reachable", e);
1473        }
1474    }
1475
1476    /**
1477     * Remove a previously registered status change listener.
1478     * @param handle the handle that was returned by {@link #addStatusChangeListener}
1479     */
1480    public static void removeStatusChangeListener(Object handle) {
1481        if (handle == null) {
1482            throw new IllegalArgumentException("you passed in a null handle");
1483        }
1484        try {
1485            getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle);
1486        } catch (RemoteException e) {
1487            // exception ignored; if this is thrown then it means the runtime is in the midst of
1488            // being restarted
1489        }
1490    }
1491
1492    /**
1493     * Returns sampling percentage for a given duration.
1494     *
1495     * Always returns at least 1%.
1496     */
1497    private int samplePercentForDuration(long durationMillis) {
1498        if (durationMillis >= SLOW_THRESHOLD_MILLIS) {
1499            return 100;
1500        }
1501        return (int) (100 * durationMillis / SLOW_THRESHOLD_MILLIS) + 1;
1502    }
1503
1504    private void maybeLogQueryToEventLog(long durationMillis,
1505                                         Uri uri, String[] projection,
1506                                         String selection, String sortOrder) {
1507        int samplePercent = samplePercentForDuration(durationMillis);
1508        if (samplePercent < 100) {
1509            synchronized (mRandom) {
1510                if (mRandom.nextInt(100) >= samplePercent) {
1511                    return;
1512                }
1513            }
1514        }
1515
1516        StringBuilder projectionBuffer = new StringBuilder(100);
1517        if (projection != null) {
1518            for (int i = 0; i < projection.length; ++i) {
1519                // Note: not using a comma delimiter here, as the
1520                // multiple arguments to EventLog.writeEvent later
1521                // stringify with a comma delimiter, which would make
1522                // parsing uglier later.
1523                if (i != 0) projectionBuffer.append('/');
1524                projectionBuffer.append(projection[i]);
1525            }
1526        }
1527
1528        // ActivityThread.currentPackageName() only returns non-null if the
1529        // current thread is an application main thread.  This parameter tells
1530        // us whether an event loop is blocked, and if so, which app it is.
1531        String blockingPackage = AppGlobals.getInitialPackage();
1532
1533        EventLog.writeEvent(
1534            EventLogTags.CONTENT_QUERY_SAMPLE,
1535            uri.toString(),
1536            projectionBuffer.toString(),
1537            selection != null ? selection : "",
1538            sortOrder != null ? sortOrder : "",
1539            durationMillis,
1540            blockingPackage != null ? blockingPackage : "",
1541            samplePercent);
1542    }
1543
1544    private void maybeLogUpdateToEventLog(
1545        long durationMillis, Uri uri, String operation, String selection) {
1546        int samplePercent = samplePercentForDuration(durationMillis);
1547        if (samplePercent < 100) {
1548            synchronized (mRandom) {
1549                if (mRandom.nextInt(100) >= samplePercent) {
1550                    return;
1551                }
1552            }
1553        }
1554        String blockingPackage = AppGlobals.getInitialPackage();
1555        EventLog.writeEvent(
1556            EventLogTags.CONTENT_UPDATE_SAMPLE,
1557            uri.toString(),
1558            operation,
1559            selection != null ? selection : "",
1560            durationMillis,
1561            blockingPackage != null ? blockingPackage : "",
1562            samplePercent);
1563    }
1564
1565    private final class CursorWrapperInner extends CursorWrapper {
1566        private final IContentProvider mContentProvider;
1567        public static final String TAG="CursorWrapperInner";
1568
1569        private final CloseGuard mCloseGuard = CloseGuard.get();
1570        private boolean mProviderReleased;
1571
1572        CursorWrapperInner(Cursor cursor, IContentProvider icp) {
1573            super(cursor);
1574            mContentProvider = icp;
1575            mCloseGuard.open("close");
1576        }
1577
1578        @Override
1579        public void close() {
1580            super.close();
1581            ContentResolver.this.releaseProvider(mContentProvider);
1582            mProviderReleased = true;
1583
1584            if (mCloseGuard != null) {
1585                mCloseGuard.close();
1586            }
1587        }
1588
1589        @Override
1590        protected void finalize() throws Throwable {
1591            try {
1592                if (mCloseGuard != null) {
1593                    mCloseGuard.warnIfOpen();
1594                }
1595
1596                if (!mProviderReleased && mContentProvider != null) {
1597                    // Even though we are using CloseGuard, log this anyway so that
1598                    // application developers always see the message in the log.
1599                    Log.w(TAG, "Cursor finalized without prior close()");
1600                    ContentResolver.this.releaseProvider(mContentProvider);
1601                }
1602            } finally {
1603                super.finalize();
1604            }
1605        }
1606    }
1607
1608    private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
1609        private final IContentProvider mContentProvider;
1610        public static final String TAG="ParcelFileDescriptorInner";
1611        private boolean mReleaseProviderFlag = false;
1612
1613        ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
1614            super(pfd);
1615            mContentProvider = icp;
1616        }
1617
1618        @Override
1619        public void close() throws IOException {
1620            if(!mReleaseProviderFlag) {
1621                super.close();
1622                ContentResolver.this.releaseProvider(mContentProvider);
1623                mReleaseProviderFlag = true;
1624            }
1625        }
1626
1627        @Override
1628        protected void finalize() throws Throwable {
1629            if (!mReleaseProviderFlag) {
1630                close();
1631            }
1632        }
1633    }
1634
1635    /** @hide */
1636    public static final String CONTENT_SERVICE_NAME = "content";
1637
1638    /** @hide */
1639    public static IContentService getContentService() {
1640        if (sContentService != null) {
1641            return sContentService;
1642        }
1643        IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
1644        if (false) Log.v("ContentService", "default service binder = " + b);
1645        sContentService = IContentService.Stub.asInterface(b);
1646        if (false) Log.v("ContentService", "default service = " + sContentService);
1647        return sContentService;
1648    }
1649
1650    private static IContentService sContentService;
1651    private final Context mContext;
1652    private static final String TAG = "ContentResolver";
1653}
1654