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