/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.content; import android.content.res.AssetFileDescriptor; import android.database.BulkCursorNative; import android.database.BulkCursorToCursorAdaptor; import android.database.Cursor; import android.database.CursorWindow; import android.database.DatabaseUtils; import android.database.IBulkCursor; import android.database.IContentObserver; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; import java.io.FileNotFoundException; import java.util.ArrayList; /** * {@hide} */ abstract public class ContentProviderNative extends Binder implements IContentProvider { private static final String TAG = "ContentProvider"; public ContentProviderNative() { attachInterface(this, descriptor); } /** * Cast a Binder object into a content resolver interface, generating * a proxy if needed. */ static public IContentProvider asInterface(IBinder obj) { if (obj == null) { return null; } IContentProvider in = (IContentProvider)obj.queryLocalInterface(descriptor); if (in != null) { return in; } return new ContentProviderProxy(obj); } @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { try { switch (code) { case QUERY_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); Uri url = Uri.CREATOR.createFromParcel(data); // String[] projection int num = data.readInt(); String[] projection = null; if (num > 0) { projection = new String[num]; for (int i = 0; i < num; i++) { projection[i] = data.readString(); } } // String selection, String[] selectionArgs... String selection = data.readString(); num = data.readInt(); String[] selectionArgs = null; if (num > 0) { selectionArgs = new String[num]; for (int i = 0; i < num; i++) { selectionArgs[i] = data.readString(); } } String sortOrder = data.readString(); IContentObserver observer = IContentObserver.Stub. asInterface(data.readStrongBinder()); CursorWindow window = CursorWindow.CREATOR.createFromParcel(data); // Flag for whether caller wants the number of // rows in the cursor and the position of the // "_id" column index (or -1 if non-existent) // Only to be returned if binder != null. boolean wantsCursorMetadata = data.readInt() != 0; IBulkCursor bulkCursor = bulkQuery(url, projection, selection, selectionArgs, sortOrder, observer, window); reply.writeNoException(); if (bulkCursor != null) { reply.writeStrongBinder(bulkCursor.asBinder()); if (wantsCursorMetadata) { reply.writeInt(bulkCursor.count()); reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex( bulkCursor.getColumnNames())); } } else { reply.writeStrongBinder(null); } return true; } case GET_TYPE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); Uri url = Uri.CREATOR.createFromParcel(data); String type = getType(url); reply.writeNoException(); reply.writeString(type); return true; } case INSERT_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); Uri url = Uri.CREATOR.createFromParcel(data); ContentValues values = ContentValues.CREATOR.createFromParcel(data); Uri out = insert(url, values); reply.writeNoException(); Uri.writeToParcel(reply, out); return true; } case BULK_INSERT_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); Uri url = Uri.CREATOR.createFromParcel(data); ContentValues[] values = data.createTypedArray(ContentValues.CREATOR); int count = bulkInsert(url, values); reply.writeNoException(); reply.writeInt(count); return true; } case APPLY_BATCH_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); final int numOperations = data.readInt(); final ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(numOperations); for (int i = 0; i < numOperations; i++) { operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data)); } final ContentProviderResult[] results = applyBatch(operations); reply.writeNoException(); reply.writeTypedArray(results, 0); return true; } case DELETE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); Uri url = Uri.CREATOR.createFromParcel(data); String selection = data.readString(); String[] selectionArgs = data.readStringArray(); int count = delete(url, selection, selectionArgs); reply.writeNoException(); reply.writeInt(count); return true; } case UPDATE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); Uri url = Uri.CREATOR.createFromParcel(data); ContentValues values = ContentValues.CREATOR.createFromParcel(data); String selection = data.readString(); String[] selectionArgs = data.readStringArray(); int count = update(url, values, selection, selectionArgs); reply.writeNoException(); reply.writeInt(count); return true; } case OPEN_FILE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); Uri url = Uri.CREATOR.createFromParcel(data); String mode = data.readString(); ParcelFileDescriptor fd; fd = openFile(url, mode); reply.writeNoException(); if (fd != null) { reply.writeInt(1); fd.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } case OPEN_ASSET_FILE_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); Uri url = Uri.CREATOR.createFromParcel(data); String mode = data.readString(); AssetFileDescriptor fd; fd = openAssetFile(url, mode); reply.writeNoException(); if (fd != null) { reply.writeInt(1); fd.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } case CALL_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); String method = data.readString(); String stringArg = data.readString(); Bundle args = data.readBundle(); Bundle responseBundle = call(method, stringArg, args); reply.writeNoException(); reply.writeBundle(responseBundle); return true; } } } catch (Exception e) { DatabaseUtils.writeExceptionToParcel(reply, e); return true; } return super.onTransact(code, data, reply, flags); } public IBinder asBinder() { return this; } } final class ContentProviderProxy implements IContentProvider { public ContentProviderProxy(IBinder remote) { mRemote = remote; } public IBinder asBinder() { return mRemote; } // Like bulkQuery() but sets up provided 'adaptor' if not null. private IBulkCursor bulkQueryInternal( Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, CursorWindow window, BulkCursorToCursorAdaptor adaptor) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); url.writeToParcel(data, 0); int length = 0; if (projection != null) { length = projection.length; } data.writeInt(length); for (int i = 0; i < length; i++) { data.writeString(projection[i]); } data.writeString(selection); if (selectionArgs != null) { length = selectionArgs.length; } else { length = 0; } data.writeInt(length); for (int i = 0; i < length; i++) { data.writeString(selectionArgs[i]); } data.writeString(sortOrder); data.writeStrongBinder(observer.asBinder()); window.writeToParcel(data, 0); // Flag for whether or not we want the number of rows in the // cursor and the position of the "_id" column index (or -1 if // non-existent). Only to be returned if binder != null. final boolean wantsCursorMetadata = (adaptor != null); data.writeInt(wantsCursorMetadata ? 1 : 0); mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); IBulkCursor bulkCursor = null; IBinder bulkCursorBinder = reply.readStrongBinder(); if (bulkCursorBinder != null) { bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder); if (wantsCursorMetadata) { int rowCount = reply.readInt(); int idColumnPosition = reply.readInt(); if (bulkCursor != null) { adaptor.set(bulkCursor, rowCount, idColumnPosition); } } } data.recycle(); reply.recycle(); return bulkCursor; } public IBulkCursor bulkQuery(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, CursorWindow window) throws RemoteException { return bulkQueryInternal( url, projection, selection, selectionArgs, sortOrder, observer, window, null /* BulkCursorToCursorAdaptor */); } public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder) throws RemoteException { //TODO make a pool of windows so we can reuse memory dealers CursorWindow window = new CursorWindow(false /* window will be used remotely */); BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); IBulkCursor bulkCursor = bulkQueryInternal( url, projection, selection, selectionArgs, sortOrder, adaptor.getObserver(), window, adaptor); if (bulkCursor == null) { return null; } return adaptor; } public String getType(Uri url) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); url.writeToParcel(data, 0); mRemote.transact(IContentProvider.GET_TYPE_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); String out = reply.readString(); data.recycle(); reply.recycle(); return out; } public Uri insert(Uri url, ContentValues values) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); url.writeToParcel(data, 0); values.writeToParcel(data, 0); mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); Uri out = Uri.CREATOR.createFromParcel(reply); data.recycle(); reply.recycle(); return out; } public int bulkInsert(Uri url, ContentValues[] values) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); url.writeToParcel(data, 0); data.writeTypedArray(values, 0); mRemote.transact(IContentProvider.BULK_INSERT_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); int count = reply.readInt(); data.recycle(); reply.recycle(); return count; } public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); data.writeInt(operations.size()); for (ContentProviderOperation operation : operations) { operation.writeToParcel(data, 0); } mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply); final ContentProviderResult[] results = reply.createTypedArray(ContentProviderResult.CREATOR); data.recycle(); reply.recycle(); return results; } public int delete(Uri url, String selection, String[] selectionArgs) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); url.writeToParcel(data, 0); data.writeString(selection); data.writeStringArray(selectionArgs); mRemote.transact(IContentProvider.DELETE_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); int count = reply.readInt(); data.recycle(); reply.recycle(); return count; } public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); url.writeToParcel(data, 0); values.writeToParcel(data, 0); data.writeString(selection); data.writeStringArray(selectionArgs); mRemote.transact(IContentProvider.UPDATE_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); int count = reply.readInt(); data.recycle(); reply.recycle(); return count; } public ParcelFileDescriptor openFile(Uri url, String mode) throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); url.writeToParcel(data, 0); data.writeString(mode); mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); int has = reply.readInt(); ParcelFileDescriptor fd = has != 0 ? reply.readFileDescriptor() : null; data.recycle(); reply.recycle(); return fd; } public AssetFileDescriptor openAssetFile(Uri url, String mode) throws RemoteException, FileNotFoundException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); url.writeToParcel(data, 0); data.writeString(mode); mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); int has = reply.readInt(); AssetFileDescriptor fd = has != 0 ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; data.recycle(); reply.recycle(); return fd; } public Bundle call(String method, String request, Bundle args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(method); data.writeString(request); data.writeBundle(args); mRemote.transact(IContentProvider.CALL_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); Bundle bundle = reply.readBundle(); data.recycle(); reply.recycle(); return bundle; } private IBinder mRemote; }