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