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