573 lines
19 KiB
Java
573 lines
19 KiB
Java
|
/*
|
||
|
* 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;
|
||
|
}
|