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