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