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