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