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