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