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