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