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