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