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