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.content.res.AssetFileDescriptor;
     20 import android.database.BulkCursorNative;
     21 import android.database.BulkCursorToCursorAdaptor;
     22 import android.database.Cursor;
     23 import android.database.CursorWindow;
     24 import android.database.DatabaseUtils;
     25 import android.database.IBulkCursor;
     26 import android.database.IContentObserver;
     27 import android.net.Uri;
     28 import android.os.Binder;
     29 import android.os.Bundle;
     30 import android.os.RemoteException;
     31 import android.os.IBinder;
     32 import android.os.Parcel;
     33 import android.os.ParcelFileDescriptor;
     34 import android.os.Parcelable;
     35 
     36 import java.io.FileNotFoundException;
     37 import java.util.ArrayList;
     38 
     39 /**
     40  * {@hide}
     41  */
     42 abstract public class ContentProviderNative extends Binder implements IContentProvider {
     43     private static final String TAG = "ContentProvider";
     44 
     45     public ContentProviderNative()
     46     {
     47         attachInterface(this, descriptor);
     48     }
     49 
     50     /**
     51      * Cast a Binder object into a content resolver interface, generating
     52      * a proxy if needed.
     53      */
     54     static public IContentProvider asInterface(IBinder obj)
     55     {
     56         if (obj == null) {
     57             return null;
     58         }
     59         IContentProvider in =
     60             (IContentProvider)obj.queryLocalInterface(descriptor);
     61         if (in != null) {
     62             return in;
     63         }
     64 
     65         return new ContentProviderProxy(obj);
     66     }
     67 
     68     @Override
     69     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
     70             throws RemoteException {
     71         try {
     72             switch (code) {
     73                 case QUERY_TRANSACTION:
     74                 {
     75                     data.enforceInterface(IContentProvider.descriptor);
     76 
     77                     Uri url = Uri.CREATOR.createFromParcel(data);
     78 
     79                     // String[] projection
     80                     int num = data.readInt();
     81                     String[] projection = null;
     82                     if (num > 0) {
     83                         projection = new String[num];
     84                         for (int i = 0; i < num; i++) {
     85                             projection[i] = data.readString();
     86                         }
     87                     }
     88 
     89                     // String selection, String[] selectionArgs...
     90                     String selection = data.readString();
     91                     num = data.readInt();
     92                     String[] selectionArgs = null;
     93                     if (num > 0) {
     94                         selectionArgs = new String[num];
     95                         for (int i = 0; i < num; i++) {
     96                             selectionArgs[i] = data.readString();
     97                         }
     98                     }
     99 
    100                     String sortOrder = data.readString();
    101                     IContentObserver observer = IContentObserver.Stub.
    102                         asInterface(data.readStrongBinder());
    103                     CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
    104 
    105                     // Flag for whether caller wants the number of
    106                     // rows in the cursor and the position of the
    107                     // "_id" column index (or -1 if non-existent)
    108                     // Only to be returned if binder != null.
    109                     boolean wantsCursorMetadata = data.readInt() != 0;
    110 
    111                     IBulkCursor bulkCursor = bulkQuery(url, projection, selection,
    112                             selectionArgs, sortOrder, observer, window);
    113                     reply.writeNoException();
    114                     if (bulkCursor != null) {
    115                         reply.writeStrongBinder(bulkCursor.asBinder());
    116 
    117                         if (wantsCursorMetadata) {
    118                             reply.writeInt(bulkCursor.count());
    119                             reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex(
    120                                 bulkCursor.getColumnNames()));
    121                         }
    122                     } else {
    123                         reply.writeStrongBinder(null);
    124                     }
    125 
    126                     return true;
    127                 }
    128 
    129                 case GET_TYPE_TRANSACTION:
    130                 {
    131                     data.enforceInterface(IContentProvider.descriptor);
    132                     Uri url = Uri.CREATOR.createFromParcel(data);
    133                     String type = getType(url);
    134                     reply.writeNoException();
    135                     reply.writeString(type);
    136 
    137                     return true;
    138                 }
    139 
    140                 case INSERT_TRANSACTION:
    141                 {
    142                     data.enforceInterface(IContentProvider.descriptor);
    143                     Uri url = Uri.CREATOR.createFromParcel(data);
    144                     ContentValues values = ContentValues.CREATOR.createFromParcel(data);
    145 
    146                     Uri out = insert(url, values);
    147                     reply.writeNoException();
    148                     Uri.writeToParcel(reply, out);
    149                     return true;
    150                 }
    151 
    152                 case BULK_INSERT_TRANSACTION:
    153                 {
    154                     data.enforceInterface(IContentProvider.descriptor);
    155                     Uri url = Uri.CREATOR.createFromParcel(data);
    156                     ContentValues[] values = data.createTypedArray(ContentValues.CREATOR);
    157 
    158                     int count = bulkInsert(url, values);
    159                     reply.writeNoException();
    160                     reply.writeInt(count);
    161                     return true;
    162                 }
    163 
    164                 case APPLY_BATCH_TRANSACTION:
    165                 {
    166                     data.enforceInterface(IContentProvider.descriptor);
    167                     final int numOperations = data.readInt();
    168                     final ArrayList<ContentProviderOperation> operations =
    169                             new ArrayList<ContentProviderOperation>(numOperations);
    170                     for (int i = 0; i < numOperations; i++) {
    171                         operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
    172                     }
    173                     final ContentProviderResult[] results = applyBatch(operations);
    174                     reply.writeNoException();
    175                     reply.writeTypedArray(results, 0);
    176                     return true;
    177                 }
    178 
    179                 case DELETE_TRANSACTION:
    180                 {
    181                     data.enforceInterface(IContentProvider.descriptor);
    182                     Uri url = Uri.CREATOR.createFromParcel(data);
    183                     String selection = data.readString();
    184                     String[] selectionArgs = data.readStringArray();
    185 
    186                     int count = delete(url, selection, selectionArgs);
    187 
    188                     reply.writeNoException();
    189                     reply.writeInt(count);
    190                     return true;
    191                 }
    192 
    193                 case UPDATE_TRANSACTION:
    194                 {
    195                     data.enforceInterface(IContentProvider.descriptor);
    196                     Uri url = Uri.CREATOR.createFromParcel(data);
    197                     ContentValues values = ContentValues.CREATOR.createFromParcel(data);
    198                     String selection = data.readString();
    199                     String[] selectionArgs = data.readStringArray();
    200 
    201                     int count = update(url, values, selection, selectionArgs);
    202 
    203                     reply.writeNoException();
    204                     reply.writeInt(count);
    205                     return true;
    206                 }
    207 
    208                 case OPEN_FILE_TRANSACTION:
    209                 {
    210                     data.enforceInterface(IContentProvider.descriptor);
    211                     Uri url = Uri.CREATOR.createFromParcel(data);
    212                     String mode = data.readString();
    213 
    214                     ParcelFileDescriptor fd;
    215                     fd = openFile(url, mode);
    216                     reply.writeNoException();
    217                     if (fd != null) {
    218                         reply.writeInt(1);
    219                         fd.writeToParcel(reply,
    220                                 Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    221                     } else {
    222                         reply.writeInt(0);
    223                     }
    224                     return true;
    225                 }
    226 
    227                 case OPEN_ASSET_FILE_TRANSACTION:
    228                 {
    229                     data.enforceInterface(IContentProvider.descriptor);
    230                     Uri url = Uri.CREATOR.createFromParcel(data);
    231                     String mode = data.readString();
    232 
    233                     AssetFileDescriptor fd;
    234                     fd = openAssetFile(url, mode);
    235                     reply.writeNoException();
    236                     if (fd != null) {
    237                         reply.writeInt(1);
    238                         fd.writeToParcel(reply,
    239                                 Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    240                     } else {
    241                         reply.writeInt(0);
    242                     }
    243                     return true;
    244                 }
    245 
    246                 case CALL_TRANSACTION:
    247                 {
    248                     data.enforceInterface(IContentProvider.descriptor);
    249 
    250                     String method = data.readString();
    251                     String stringArg = data.readString();
    252                     Bundle args = data.readBundle();
    253 
    254                     Bundle responseBundle = call(method, stringArg, args);
    255 
    256                     reply.writeNoException();
    257                     reply.writeBundle(responseBundle);
    258                     return true;
    259                 }
    260             }
    261         } catch (Exception e) {
    262             DatabaseUtils.writeExceptionToParcel(reply, e);
    263             return true;
    264         }
    265 
    266         return super.onTransact(code, data, reply, flags);
    267     }
    268 
    269     public IBinder asBinder()
    270     {
    271         return this;
    272     }
    273 }
    274 
    275 
    276 final class ContentProviderProxy implements IContentProvider
    277 {
    278     public ContentProviderProxy(IBinder remote)
    279     {
    280         mRemote = remote;
    281     }
    282 
    283     public IBinder asBinder()
    284     {
    285         return mRemote;
    286     }
    287 
    288     // Like bulkQuery() but sets up provided 'adaptor' if not null.
    289     private IBulkCursor bulkQueryInternal(
    290         Uri url, String[] projection,
    291         String selection, String[] selectionArgs, String sortOrder,
    292         IContentObserver observer, CursorWindow window,
    293         BulkCursorToCursorAdaptor adaptor) throws RemoteException {
    294         Parcel data = Parcel.obtain();
    295         Parcel reply = Parcel.obtain();
    296 
    297         data.writeInterfaceToken(IContentProvider.descriptor);
    298 
    299         url.writeToParcel(data, 0);
    300         int length = 0;
    301         if (projection != null) {
    302             length = projection.length;
    303         }
    304         data.writeInt(length);
    305         for (int i = 0; i < length; i++) {
    306             data.writeString(projection[i]);
    307         }
    308         data.writeString(selection);
    309         if (selectionArgs != null) {
    310             length = selectionArgs.length;
    311         } else {
    312             length = 0;
    313         }
    314         data.writeInt(length);
    315         for (int i = 0; i < length; i++) {
    316             data.writeString(selectionArgs[i]);
    317         }
    318         data.writeString(sortOrder);
    319         data.writeStrongBinder(observer.asBinder());
    320         window.writeToParcel(data, 0);
    321 
    322         // Flag for whether or not we want the number of rows in the
    323         // cursor and the position of the "_id" column index (or -1 if
    324         // non-existent).  Only to be returned if binder != null.
    325         final boolean wantsCursorMetadata = (adaptor != null);
    326         data.writeInt(wantsCursorMetadata ? 1 : 0);
    327 
    328         mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
    329 
    330         DatabaseUtils.readExceptionFromParcel(reply);
    331 
    332         IBulkCursor bulkCursor = null;
    333         IBinder bulkCursorBinder = reply.readStrongBinder();
    334         if (bulkCursorBinder != null) {
    335             bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);
    336 
    337             if (wantsCursorMetadata) {
    338                 int rowCount = reply.readInt();
    339                 int idColumnPosition = reply.readInt();
    340                 if (bulkCursor != null) {
    341                     adaptor.set(bulkCursor, rowCount, idColumnPosition);
    342                 }
    343             }
    344         }
    345 
    346         data.recycle();
    347         reply.recycle();
    348 
    349         return bulkCursor;
    350     }
    351 
    352     public IBulkCursor bulkQuery(Uri url, String[] projection,
    353             String selection, String[] selectionArgs, String sortOrder, IContentObserver observer,
    354             CursorWindow window) throws RemoteException {
    355         return bulkQueryInternal(
    356             url, projection, selection, selectionArgs, sortOrder,
    357             observer, window,
    358             null /* BulkCursorToCursorAdaptor */);
    359     }
    360 
    361     public Cursor query(Uri url, String[] projection, String selection,
    362             String[] selectionArgs, String sortOrder) throws RemoteException {
    363         //TODO make a pool of windows so we can reuse memory dealers
    364         CursorWindow window = new CursorWindow(false /* window will be used remotely */);
    365         BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
    366         IBulkCursor bulkCursor = bulkQueryInternal(
    367             url, projection, selection, selectionArgs, sortOrder,
    368             adaptor.getObserver(), window,
    369             adaptor);
    370         if (bulkCursor == null) {
    371             return null;
    372         }
    373         return adaptor;
    374     }
    375 
    376     public String getType(Uri url) throws RemoteException
    377     {
    378         Parcel data = Parcel.obtain();
    379         Parcel reply = Parcel.obtain();
    380 
    381         data.writeInterfaceToken(IContentProvider.descriptor);
    382 
    383         url.writeToParcel(data, 0);
    384 
    385         mRemote.transact(IContentProvider.GET_TYPE_TRANSACTION, data, reply, 0);
    386 
    387         DatabaseUtils.readExceptionFromParcel(reply);
    388         String out = reply.readString();
    389 
    390         data.recycle();
    391         reply.recycle();
    392 
    393         return out;
    394     }
    395 
    396     public Uri insert(Uri url, ContentValues values) throws RemoteException
    397     {
    398         Parcel data = Parcel.obtain();
    399         Parcel reply = Parcel.obtain();
    400 
    401         data.writeInterfaceToken(IContentProvider.descriptor);
    402 
    403         url.writeToParcel(data, 0);
    404         values.writeToParcel(data, 0);
    405 
    406         mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0);
    407 
    408         DatabaseUtils.readExceptionFromParcel(reply);
    409         Uri out = Uri.CREATOR.createFromParcel(reply);
    410 
    411         data.recycle();
    412         reply.recycle();
    413 
    414         return out;
    415     }
    416 
    417     public int bulkInsert(Uri url, ContentValues[] values) throws RemoteException {
    418         Parcel data = Parcel.obtain();
    419         Parcel reply = Parcel.obtain();
    420 
    421         data.writeInterfaceToken(IContentProvider.descriptor);
    422 
    423         url.writeToParcel(data, 0);
    424         data.writeTypedArray(values, 0);
    425 
    426         mRemote.transact(IContentProvider.BULK_INSERT_TRANSACTION, data, reply, 0);
    427 
    428         DatabaseUtils.readExceptionFromParcel(reply);
    429         int count = reply.readInt();
    430 
    431         data.recycle();
    432         reply.recycle();
    433 
    434         return count;
    435     }
    436 
    437     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
    438             throws RemoteException, OperationApplicationException {
    439         Parcel data = Parcel.obtain();
    440         Parcel reply = Parcel.obtain();
    441 
    442         data.writeInterfaceToken(IContentProvider.descriptor);
    443         data.writeInt(operations.size());
    444         for (ContentProviderOperation operation : operations) {
    445             operation.writeToParcel(data, 0);
    446         }
    447         mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0);
    448 
    449         DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply);
    450         final ContentProviderResult[] results =
    451                 reply.createTypedArray(ContentProviderResult.CREATOR);
    452 
    453         data.recycle();
    454         reply.recycle();
    455 
    456         return results;
    457     }
    458 
    459     public int delete(Uri url, String selection, String[] selectionArgs)
    460             throws RemoteException {
    461         Parcel data = Parcel.obtain();
    462         Parcel reply = Parcel.obtain();
    463 
    464         data.writeInterfaceToken(IContentProvider.descriptor);
    465 
    466         url.writeToParcel(data, 0);
    467         data.writeString(selection);
    468         data.writeStringArray(selectionArgs);
    469 
    470         mRemote.transact(IContentProvider.DELETE_TRANSACTION, data, reply, 0);
    471 
    472         DatabaseUtils.readExceptionFromParcel(reply);
    473         int count = reply.readInt();
    474 
    475         data.recycle();
    476         reply.recycle();
    477 
    478         return count;
    479     }
    480 
    481     public int update(Uri url, ContentValues values, String selection,
    482             String[] selectionArgs) throws RemoteException {
    483         Parcel data = Parcel.obtain();
    484         Parcel reply = Parcel.obtain();
    485 
    486         data.writeInterfaceToken(IContentProvider.descriptor);
    487 
    488         url.writeToParcel(data, 0);
    489         values.writeToParcel(data, 0);
    490         data.writeString(selection);
    491         data.writeStringArray(selectionArgs);
    492 
    493         mRemote.transact(IContentProvider.UPDATE_TRANSACTION, data, reply, 0);
    494 
    495         DatabaseUtils.readExceptionFromParcel(reply);
    496         int count = reply.readInt();
    497 
    498         data.recycle();
    499         reply.recycle();
    500 
    501         return count;
    502     }
    503 
    504     public ParcelFileDescriptor openFile(Uri url, String mode)
    505             throws RemoteException, FileNotFoundException {
    506         Parcel data = Parcel.obtain();
    507         Parcel reply = Parcel.obtain();
    508 
    509         data.writeInterfaceToken(IContentProvider.descriptor);
    510 
    511         url.writeToParcel(data, 0);
    512         data.writeString(mode);
    513 
    514         mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0);
    515 
    516         DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
    517         int has = reply.readInt();
    518         ParcelFileDescriptor fd = has != 0 ? reply.readFileDescriptor() : null;
    519 
    520         data.recycle();
    521         reply.recycle();
    522 
    523         return fd;
    524     }
    525 
    526     public AssetFileDescriptor openAssetFile(Uri url, String mode)
    527             throws RemoteException, FileNotFoundException {
    528         Parcel data = Parcel.obtain();
    529         Parcel reply = Parcel.obtain();
    530 
    531         data.writeInterfaceToken(IContentProvider.descriptor);
    532 
    533         url.writeToParcel(data, 0);
    534         data.writeString(mode);
    535 
    536         mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0);
    537 
    538         DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
    539         int has = reply.readInt();
    540         AssetFileDescriptor fd = has != 0
    541                 ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null;
    542 
    543         data.recycle();
    544         reply.recycle();
    545 
    546         return fd;
    547     }
    548 
    549     public Bundle call(String method, String request, Bundle args)
    550             throws RemoteException {
    551         Parcel data = Parcel.obtain();
    552         Parcel reply = Parcel.obtain();
    553 
    554         data.writeInterfaceToken(IContentProvider.descriptor);
    555 
    556         data.writeString(method);
    557         data.writeString(request);
    558         data.writeBundle(args);
    559 
    560         mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0);
    561 
    562         DatabaseUtils.readExceptionFromParcel(reply);
    563         Bundle bundle = reply.readBundle();
    564 
    565         data.recycle();
    566         reply.recycle();
    567 
    568         return bundle;
    569     }
    570 
    571     private IBinder mRemote;
    572 }
    573