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