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