Revert "Fix ownership of CursorWindows across processes."
This reverts commit d2183654e0.
This commit is contained in:
@@ -22,6 +22,10 @@ import android.content.pm.ProviderInfo;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorToBulkCursorAdaptor;
|
||||
import android.database.CursorWindow;
|
||||
import android.database.IBulkCursor;
|
||||
import android.database.IContentObserver;
|
||||
import android.database.SQLException;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
@@ -167,9 +171,22 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
|
||||
return ContentProvider.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderName() {
|
||||
return getContentProvider().getClass().getName();
|
||||
/**
|
||||
* Remote version of a query, which returns an IBulkCursor. The bulk
|
||||
* cursor should be wrapped with BulkCursorToCursorAdaptor before use.
|
||||
*/
|
||||
public IBulkCursor bulkQuery(Uri uri, String[] projection,
|
||||
String selection, String[] selectionArgs, String sortOrder,
|
||||
IContentObserver observer, CursorWindow window) {
|
||||
enforceReadPermission(uri);
|
||||
Cursor cursor = ContentProvider.this.query(uri, projection,
|
||||
selection, selectionArgs, sortOrder);
|
||||
if (cursor == null) {
|
||||
return null;
|
||||
}
|
||||
return new CursorToBulkCursorAdaptor(cursor, observer,
|
||||
ContentProvider.this.getClass().getName(),
|
||||
hasWritePermission(uri), window);
|
||||
}
|
||||
|
||||
public Cursor query(Uri uri, String[] projection,
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.content.res.AssetFileDescriptor;
|
||||
import android.database.BulkCursorNative;
|
||||
import android.database.BulkCursorToCursorAdaptor;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorToBulkCursorAdaptor;
|
||||
import android.database.CursorWindow;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.IBulkCursor;
|
||||
@@ -66,13 +65,6 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
|
||||
return new ContentProviderProxy(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the content provider.
|
||||
* Should probably be part of the {@link IContentProvider} interface.
|
||||
* @return The content provider name.
|
||||
*/
|
||||
public abstract String getProviderName();
|
||||
|
||||
@Override
|
||||
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
|
||||
throws RemoteException {
|
||||
@@ -106,23 +98,33 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
|
||||
}
|
||||
|
||||
String sortOrder = data.readString();
|
||||
IContentObserver observer = IContentObserver.Stub.asInterface(
|
||||
data.readStrongBinder());
|
||||
IContentObserver observer = IContentObserver.Stub.
|
||||
asInterface(data.readStrongBinder());
|
||||
CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
|
||||
|
||||
Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder);
|
||||
if (cursor != null) {
|
||||
CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
|
||||
cursor, observer, getProviderName(), window);
|
||||
final IBinder binder = adaptor.asBinder();
|
||||
final int count = adaptor.count();
|
||||
final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex(
|
||||
adaptor.getColumnNames());
|
||||
// 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;
|
||||
|
||||
reply.writeNoException();
|
||||
reply.writeStrongBinder(binder);
|
||||
reply.writeInt(count);
|
||||
reply.writeInt(index);
|
||||
IBulkCursor bulkCursor = bulkQuery(url, projection, selection,
|
||||
selectionArgs, sortOrder, observer, window);
|
||||
if (bulkCursor != null) {
|
||||
final IBinder binder = bulkCursor.asBinder();
|
||||
if (wantsCursorMetadata) {
|
||||
final int count = bulkCursor.count();
|
||||
final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex(
|
||||
bulkCursor.getColumnNames());
|
||||
|
||||
reply.writeNoException();
|
||||
reply.writeStrongBinder(binder);
|
||||
reply.writeInt(count);
|
||||
reply.writeInt(index);
|
||||
} else {
|
||||
reply.writeNoException();
|
||||
reply.writeStrongBinder(binder);
|
||||
}
|
||||
} else {
|
||||
reply.writeNoException();
|
||||
reply.writeStrongBinder(null);
|
||||
@@ -322,72 +324,94 @@ final class ContentProviderProxy implements IContentProvider
|
||||
return mRemote;
|
||||
}
|
||||
|
||||
public Cursor query(Uri url, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) throws RemoteException {
|
||||
CursorWindow window = new CursorWindow(false /* window will be used remotely */);
|
||||
// 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();
|
||||
try {
|
||||
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
try {
|
||||
data.writeInterfaceToken(IContentProvider.descriptor);
|
||||
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(adaptor.getObserver().asBinder());
|
||||
window.writeToParcel(data, 0);
|
||||
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);
|
||||
|
||||
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 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);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
|
||||
|
||||
IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder());
|
||||
if (bulkCursor != null) {
|
||||
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();
|
||||
adaptor.initialize(bulkCursor, rowCount, idColumnPosition);
|
||||
} else {
|
||||
adaptor.close();
|
||||
adaptor = null;
|
||||
if (bulkCursor != null) {
|
||||
adaptor.set(bulkCursor, rowCount, idColumnPosition);
|
||||
}
|
||||
}
|
||||
return adaptor;
|
||||
} catch (RemoteException ex) {
|
||||
adaptor.close();
|
||||
throw ex;
|
||||
} catch (RuntimeException ex) {
|
||||
adaptor.close();
|
||||
throw ex;
|
||||
} finally {
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
return bulkCursor;
|
||||
} finally {
|
||||
// We close the window now because the cursor adaptor does not
|
||||
// take ownership of the window until the first call to onMove.
|
||||
// The adaptor will obtain a fresh reference to the window when
|
||||
// it is filled.
|
||||
window.close();
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -18,6 +18,9 @@ package android.content;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWindow;
|
||||
import android.database.IBulkCursor;
|
||||
import android.database.IContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -33,6 +36,13 @@ import java.util.ArrayList;
|
||||
* @hide
|
||||
*/
|
||||
public interface IContentProvider extends IInterface {
|
||||
/**
|
||||
* @hide - hide this because return type IBulkCursor and parameter
|
||||
* IContentObserver are system private classes.
|
||||
*/
|
||||
public IBulkCursor bulkQuery(Uri url, String[] projection,
|
||||
String selection, String[] selectionArgs, String sortOrder, IContentObserver observer,
|
||||
CursorWindow window) throws RemoteException;
|
||||
public Cursor query(Uri url, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) throws RemoteException;
|
||||
public String getType(Uri url) throws RemoteException;
|
||||
|
||||
@@ -78,11 +78,13 @@ public abstract class AbstractCursor implements CrossProcessCursor {
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
onDeactivateOrClose();
|
||||
deactivateInternal();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
protected void onDeactivateOrClose() {
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void deactivateInternal() {
|
||||
if (mSelfObserver != null) {
|
||||
mContentResolver.unregisterContentObserver(mSelfObserver);
|
||||
mSelfObserverRegistered = false;
|
||||
@@ -106,7 +108,7 @@ public abstract class AbstractCursor implements CrossProcessCursor {
|
||||
public void close() {
|
||||
mClosed = true;
|
||||
mContentObservable.unregisterAll();
|
||||
onDeactivateOrClose();
|
||||
deactivateInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,11 +19,6 @@ package android.database;
|
||||
/**
|
||||
* A base class for Cursors that store their data in {@link CursorWindow}s.
|
||||
* <p>
|
||||
* The cursor owns the cursor window it uses. When the cursor is closed,
|
||||
* its window is also closed. Likewise, when the window used by the cursor is
|
||||
* changed, its old window is closed. This policy of strict ownership ensures
|
||||
* that cursor windows are not leaked.
|
||||
* </p><p>
|
||||
* Subclasses are responsible for filling the cursor window with data during
|
||||
* {@link #onMove(int, int)}, allocating a new cursor window if necessary.
|
||||
* During {@link #requery()}, the existing cursor window should be cleared and
|
||||
@@ -185,25 +180,4 @@ public abstract class AbstractWindowedCursor extends AbstractCursor {
|
||||
mWindow = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a window, clear it.
|
||||
* Otherwise, creates a local window.
|
||||
* @hide
|
||||
*/
|
||||
protected void clearOrCreateLocalWindow() {
|
||||
if (mWindow == null) {
|
||||
// If there isn't a window set already it will only be accessed locally
|
||||
mWindow = new CursorWindow(true /* the window is local only */);
|
||||
} else {
|
||||
mWindow.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
protected void onDeactivateOrClose() {
|
||||
super.onDeactivateOrClose();
|
||||
closeWindow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,13 +62,13 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
int startPos = data.readInt();
|
||||
CursorWindow window = getWindow(startPos);
|
||||
reply.writeNoException();
|
||||
if (window == null) {
|
||||
reply.writeInt(0);
|
||||
} else {
|
||||
reply.writeInt(1);
|
||||
window.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||
return true;
|
||||
}
|
||||
reply.writeNoException();
|
||||
reply.writeInt(1);
|
||||
window.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,24 +21,40 @@ import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local process.
|
||||
* Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local
|
||||
* process.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
private static final String TAG = "BulkCursor";
|
||||
|
||||
private SelfContentObserver mObserverBridge = new SelfContentObserver(this);
|
||||
private SelfContentObserver mObserverBridge;
|
||||
private IBulkCursor mBulkCursor;
|
||||
private int mCount;
|
||||
private String[] mColumns;
|
||||
private boolean mWantsAllOnMoveCalls;
|
||||
|
||||
public void set(IBulkCursor bulkCursor) {
|
||||
mBulkCursor = bulkCursor;
|
||||
|
||||
try {
|
||||
mCount = mBulkCursor.count();
|
||||
mWantsAllOnMoveCalls = mBulkCursor.getWantsAllOnMoveCalls();
|
||||
|
||||
// Search for the rowID column index and set it for our parent
|
||||
mColumns = mBulkCursor.getColumnNames();
|
||||
mRowIdColumnIndex = findRowIdColumnIndex(mColumns);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Setup failed because the remote process is dead");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the adaptor.
|
||||
* Must be called before first use.
|
||||
* Version of set() that does fewer Binder calls if the caller
|
||||
* already knows BulkCursorToCursorAdaptor's properties.
|
||||
*/
|
||||
public void initialize(IBulkCursor bulkCursor, int count, int idIndex) {
|
||||
public void set(IBulkCursor bulkCursor, int count, int idIndex) {
|
||||
mBulkCursor = bulkCursor;
|
||||
mColumns = null; // lazily retrieved
|
||||
mCount = count;
|
||||
@@ -64,37 +80,31 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
*
|
||||
* @return A SelfContentObserver hooked up to this Cursor
|
||||
*/
|
||||
public IContentObserver getObserver() {
|
||||
return mObserverBridge.getContentObserver();
|
||||
}
|
||||
|
||||
private void throwIfCursorIsClosed() {
|
||||
if (mBulkCursor == null) {
|
||||
throw new StaleDataException("Attempted to access a cursor after it has been closed.");
|
||||
public synchronized IContentObserver getObserver() {
|
||||
if (mObserverBridge == null) {
|
||||
mObserverBridge = new SelfContentObserver(this);
|
||||
}
|
||||
return mObserverBridge.getContentObserver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
throwIfCursorIsClosed();
|
||||
return mCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(int oldPosition, int newPosition) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
try {
|
||||
// Make sure we have the proper window
|
||||
if (mWindow != null) {
|
||||
if (newPosition < mWindow.getStartPosition() ||
|
||||
newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
|
||||
setWindow(mBulkCursor.getWindow(newPosition));
|
||||
mWindow = mBulkCursor.getWindow(newPosition);
|
||||
} else if (mWantsAllOnMoveCalls) {
|
||||
mBulkCursor.onMove(newPosition);
|
||||
}
|
||||
} else {
|
||||
setWindow(mBulkCursor.getWindow(newPosition));
|
||||
mWindow = mBulkCursor.getWindow(newPosition);
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
// We tried to get a window and failed
|
||||
@@ -116,54 +126,44 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
// which is what actually makes the data set invalid.
|
||||
super.deactivate();
|
||||
|
||||
if (mBulkCursor != null) {
|
||||
try {
|
||||
mBulkCursor.deactivate();
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Remote process exception when deactivating");
|
||||
}
|
||||
try {
|
||||
mBulkCursor.deactivate();
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Remote process exception when deactivating");
|
||||
}
|
||||
mWindow = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
|
||||
if (mBulkCursor != null) {
|
||||
try {
|
||||
mBulkCursor.close();
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Remote process exception when closing");
|
||||
} finally {
|
||||
mBulkCursor = null;
|
||||
}
|
||||
try {
|
||||
mBulkCursor.close();
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Remote process exception when closing");
|
||||
}
|
||||
mWindow = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requery() {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
try {
|
||||
CursorWindow newWindow = new CursorWindow(false /* create a remote window */);
|
||||
try {
|
||||
mCount = mBulkCursor.requery(getObserver(), newWindow);
|
||||
if (mCount != -1) {
|
||||
mPos = -1;
|
||||
closeWindow();
|
||||
int oldCount = mCount;
|
||||
//TODO get the window from a pool somewhere to avoid creating the memory dealer
|
||||
mCount = mBulkCursor.requery(getObserver(), new CursorWindow(
|
||||
false /* the window will be accessed across processes */));
|
||||
if (mCount != -1) {
|
||||
mPos = -1;
|
||||
closeWindow();
|
||||
|
||||
// super.requery() will call onChanged. Do it here instead of relying on the
|
||||
// observer from the far side so that observers can see a correct value for mCount
|
||||
// when responding to onChanged.
|
||||
super.requery();
|
||||
return true;
|
||||
} else {
|
||||
deactivate();
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
// Don't take ownership of the window until the next call to onMove.
|
||||
newWindow.close();
|
||||
// super.requery() will call onChanged. Do it here instead of relying on the
|
||||
// observer from the far side so that observers can see a correct value for mCount
|
||||
// when responding to onChanged.
|
||||
super.requery();
|
||||
return true;
|
||||
} else {
|
||||
deactivate();
|
||||
return false;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage());
|
||||
@@ -174,8 +174,6 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
if (mColumns == null) {
|
||||
try {
|
||||
mColumns = mBulkCursor.getColumnNames();
|
||||
@@ -189,8 +187,6 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
|
||||
@Override
|
||||
public Bundle getExtras() {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
try {
|
||||
return mBulkCursor.getExtras();
|
||||
} catch (RemoteException e) {
|
||||
@@ -202,8 +198,6 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
|
||||
@Override
|
||||
public Bundle respond(Bundle extras) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
try {
|
||||
return mBulkCursor.respond(extras);
|
||||
} catch (RemoteException e) {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package android.database;
|
||||
|
||||
public interface CrossProcessCursor extends Cursor {
|
||||
public interface CrossProcessCursor extends Cursor{
|
||||
/**
|
||||
* returns a pre-filled window, return NULL if no such window
|
||||
*/
|
||||
|
||||
@@ -24,37 +24,19 @@ import android.util.Log;
|
||||
|
||||
/**
|
||||
* Wraps a BulkCursor around an existing Cursor making it remotable.
|
||||
* <p>
|
||||
* If the wrapped cursor is a {@link AbstractWindowedCursor} then it owns
|
||||
* the cursor window. Otherwise, the adaptor takes ownership of the
|
||||
* cursor itself and ensures it gets closed as needed during deactivation
|
||||
* and requeries.
|
||||
* </p>
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
implements IBinder.DeathRecipient {
|
||||
private static final String TAG = "Cursor";
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final CrossProcessCursor mCursor;
|
||||
private CursorWindow mWindow;
|
||||
private final String mProviderName;
|
||||
private ContentObserverProxy mObserver;
|
||||
|
||||
/**
|
||||
* The cursor that is being adapted.
|
||||
* This field is set to null when the cursor is closed.
|
||||
*/
|
||||
private CrossProcessCursor mCursor;
|
||||
|
||||
/**
|
||||
* The cursor window used by the cross process cursor.
|
||||
* This field is always null for abstract windowed cursors since they are responsible
|
||||
* for managing the lifetime of their window.
|
||||
*/
|
||||
private CursorWindow mWindowForNonWindowedCursor;
|
||||
|
||||
private static final class ContentObserverProxy extends ContentObserver {
|
||||
private static final class ContentObserverProxy extends ContentObserver
|
||||
{
|
||||
protected IContentObserver mRemote;
|
||||
|
||||
public ContentObserverProxy(IContentObserver remoteObserver, DeathRecipient recipient) {
|
||||
@@ -88,7 +70,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
}
|
||||
|
||||
public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName,
|
||||
CursorWindow window) {
|
||||
boolean allowWrite, CursorWindow window) {
|
||||
try {
|
||||
mCursor = (CrossProcessCursor) cursor;
|
||||
if (mCursor instanceof AbstractWindowedCursor) {
|
||||
@@ -99,167 +81,90 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
+ providerName, new RuntimeException());
|
||||
}
|
||||
}
|
||||
windowedCursor.setWindow(window); // cursor takes ownership of window
|
||||
windowedCursor.setWindow(window);
|
||||
} else {
|
||||
mWindowForNonWindowedCursor = window; // we own the window
|
||||
mWindow = window;
|
||||
mCursor.fillWindow(0, window);
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
// TODO Implement this case.
|
||||
window.close();
|
||||
throw new UnsupportedOperationException(
|
||||
"Only CrossProcessCursor cursors are supported across process for now", e);
|
||||
}
|
||||
mProviderName = providerName;
|
||||
|
||||
synchronized (mLock) {
|
||||
createAndRegisterObserverProxyLocked(observer);
|
||||
}
|
||||
createAndRegisterObserverProxy(observer);
|
||||
}
|
||||
|
||||
private void closeCursorAndWindowLocked() {
|
||||
if (mCursor != null) {
|
||||
unregisterObserverProxyLocked();
|
||||
mCursor.close();
|
||||
mCursor = null;
|
||||
}
|
||||
|
||||
if (mWindowForNonWindowedCursor != null) {
|
||||
mWindowForNonWindowedCursor.close();
|
||||
mWindowForNonWindowedCursor = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void throwIfCursorIsClosed() {
|
||||
if (mCursor == null) {
|
||||
throw new StaleDataException("Attempted to access a cursor after it has been closed.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void binderDied() {
|
||||
synchronized (mLock) {
|
||||
closeCursorAndWindowLocked();
|
||||
mCursor.close();
|
||||
if (mWindow != null) {
|
||||
mWindow.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public CursorWindow getWindow(int startPos) {
|
||||
synchronized (mLock) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
mCursor.moveToPosition(startPos);
|
||||
|
||||
final CursorWindow window;
|
||||
if (mCursor instanceof AbstractWindowedCursor) {
|
||||
window = ((AbstractWindowedCursor)mCursor).getWindow();
|
||||
} else {
|
||||
window = mWindowForNonWindowedCursor;
|
||||
if (window != null
|
||||
&& (startPos < window.getStartPosition() ||
|
||||
startPos >= (window.getStartPosition() + window.getNumRows()))) {
|
||||
mCursor.fillWindow(startPos, window);
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire a reference before returning from this RPC.
|
||||
// The Binder proxy will decrement the reference count again as part of writing
|
||||
// the CursorWindow to the reply parcel as a return value.
|
||||
if (window != null) {
|
||||
window.acquireReference();
|
||||
}
|
||||
return window;
|
||||
mCursor.moveToPosition(startPos);
|
||||
|
||||
if (mWindow != null) {
|
||||
if (startPos < mWindow.getStartPosition() ||
|
||||
startPos >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
|
||||
mCursor.fillWindow(startPos, mWindow);
|
||||
}
|
||||
return mWindow;
|
||||
} else {
|
||||
return ((AbstractWindowedCursor)mCursor).getWindow();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMove(int position) {
|
||||
synchronized (mLock) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
mCursor.onMove(mCursor.getPosition(), position);
|
||||
}
|
||||
mCursor.onMove(mCursor.getPosition(), position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count() {
|
||||
synchronized (mLock) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
return mCursor.getCount();
|
||||
}
|
||||
return mCursor.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
synchronized (mLock) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
return mCursor.getColumnNames();
|
||||
}
|
||||
return mCursor.getColumnNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
synchronized (mLock) {
|
||||
if (mCursor != null) {
|
||||
unregisterObserverProxyLocked();
|
||||
mCursor.deactivate();
|
||||
}
|
||||
}
|
||||
maybeUnregisterObserverProxy();
|
||||
mCursor.deactivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (mLock) {
|
||||
closeCursorAndWindowLocked();
|
||||
}
|
||||
maybeUnregisterObserverProxy();
|
||||
mCursor.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requery(IContentObserver observer, CursorWindow window) {
|
||||
synchronized (mLock) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
if (mCursor instanceof AbstractWindowedCursor) {
|
||||
((AbstractWindowedCursor) mCursor).setWindow(window);
|
||||
} else {
|
||||
if (mWindowForNonWindowedCursor != null) {
|
||||
mWindowForNonWindowedCursor.close();
|
||||
}
|
||||
mWindowForNonWindowedCursor = window;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!mCursor.requery()) {
|
||||
return -1;
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
IllegalStateException leakProgram = new IllegalStateException(
|
||||
mProviderName + " Requery misuse db, mCursor isClosed:" +
|
||||
mCursor.isClosed(), e);
|
||||
throw leakProgram;
|
||||
}
|
||||
|
||||
if (!(mCursor instanceof AbstractWindowedCursor)) {
|
||||
if (window != null) {
|
||||
mCursor.fillWindow(0, window);
|
||||
}
|
||||
}
|
||||
|
||||
unregisterObserverProxyLocked();
|
||||
createAndRegisterObserverProxyLocked(observer);
|
||||
return mCursor.getCount();
|
||||
if (mWindow == null) {
|
||||
((AbstractWindowedCursor)mCursor).setWindow(window);
|
||||
}
|
||||
try {
|
||||
if (!mCursor.requery()) {
|
||||
return -1;
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
IllegalStateException leakProgram = new IllegalStateException(
|
||||
mProviderName + " Requery misuse db, mCursor isClosed:" +
|
||||
mCursor.isClosed(), e);
|
||||
throw leakProgram;
|
||||
}
|
||||
|
||||
if (mWindow != null) {
|
||||
mCursor.fillWindow(0, window);
|
||||
mWindow = window;
|
||||
}
|
||||
maybeUnregisterObserverProxy();
|
||||
createAndRegisterObserverProxy(observer);
|
||||
return mCursor.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getWantsAllOnMoveCalls() {
|
||||
synchronized (mLock) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
return mCursor.getWantsAllOnMoveCalls();
|
||||
}
|
||||
return mCursor.getWantsAllOnMoveCalls();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,7 +173,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
* @param observer the IContentObserver that wants to monitor the cursor
|
||||
* @throws IllegalStateException if an observer is already registered
|
||||
*/
|
||||
private void createAndRegisterObserverProxyLocked(IContentObserver observer) {
|
||||
private void createAndRegisterObserverProxy(IContentObserver observer) {
|
||||
if (mObserver != null) {
|
||||
throw new IllegalStateException("an observer is already registered");
|
||||
}
|
||||
@@ -277,7 +182,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
}
|
||||
|
||||
/** Unregister the observer if it is already registered. */
|
||||
private void unregisterObserverProxyLocked() {
|
||||
private void maybeUnregisterObserverProxy() {
|
||||
if (mObserver != null) {
|
||||
mCursor.unregisterContentObserver(mObserver);
|
||||
mObserver.unlinkToDeath(this);
|
||||
@@ -285,21 +190,11 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getExtras() {
|
||||
synchronized (mLock) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
return mCursor.getExtras();
|
||||
}
|
||||
return mCursor.getExtras();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle respond(Bundle extras) {
|
||||
synchronized (mLock) {
|
||||
throwIfCursorIsClosed();
|
||||
|
||||
return mCursor.respond(extras);
|
||||
}
|
||||
return mCursor.respond(extras);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package android.database;
|
||||
|
||||
import dalvik.system.CloseGuard;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.database.sqlite.SQLiteClosable;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
@@ -50,8 +48,6 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
|
||||
private int mStartPos;
|
||||
|
||||
private final CloseGuard mCloseGuard = CloseGuard.get();
|
||||
|
||||
private static native int nativeInitializeEmpty(int cursorWindowSize, boolean localOnly);
|
||||
private static native int nativeInitializeFromBinder(IBinder nativeBinder);
|
||||
private static native void nativeDispose(int windowPtr);
|
||||
@@ -95,7 +91,6 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
throw new CursorWindowAllocationException("Cursor window allocation of " +
|
||||
(sCursorWindowSize / 1024) + " kb failed. " + printStats());
|
||||
}
|
||||
mCloseGuard.open("close");
|
||||
recordNewWindow(Binder.getCallingPid(), mWindowPtr);
|
||||
}
|
||||
|
||||
@@ -107,15 +102,11 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
throw new CursorWindowAllocationException("Cursor window could not be "
|
||||
+ "created from binder.");
|
||||
}
|
||||
mCloseGuard.open("close");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
if (mCloseGuard != null) {
|
||||
mCloseGuard.warnIfOpen();
|
||||
}
|
||||
dispose();
|
||||
} finally {
|
||||
super.finalize();
|
||||
@@ -123,9 +114,6 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
}
|
||||
|
||||
private void dispose() {
|
||||
if (mCloseGuard != null) {
|
||||
mCloseGuard.close();
|
||||
}
|
||||
if (mWindowPtr != 0) {
|
||||
recordClosingOfWindow(mWindowPtr);
|
||||
nativeDispose(mWindowPtr);
|
||||
@@ -689,10 +677,6 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeStrongBinder(nativeGetBinder(mWindowPtr));
|
||||
dest.writeInt(mStartPos);
|
||||
|
||||
if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -89,6 +89,8 @@ public class SQLiteCursor extends AbstractWindowedCursor {
|
||||
* @param query the {@link SQLiteQuery} object associated with this cursor object.
|
||||
*/
|
||||
public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
|
||||
// The AbstractCursor constructor needs to do some setup.
|
||||
super();
|
||||
if (query == null) {
|
||||
throw new IllegalArgumentException("query object cannot be null");
|
||||
}
|
||||
@@ -155,7 +157,12 @@ public class SQLiteCursor extends AbstractWindowedCursor {
|
||||
}
|
||||
|
||||
private void fillWindow(int startPos) {
|
||||
clearOrCreateLocalWindow();
|
||||
if (mWindow == null) {
|
||||
// If there isn't a window set already it will only be accessed locally
|
||||
mWindow = new CursorWindow(true /* the window is local only */);
|
||||
} else {
|
||||
mWindow.clear();
|
||||
}
|
||||
mWindow.setStartPosition(startPos);
|
||||
int count = getQuery().fillWindow(mWindow);
|
||||
if (startPos == 0) { // fillWindow returns count(*) only for startPos = 0
|
||||
@@ -207,9 +214,16 @@ public class SQLiteCursor extends AbstractWindowedCursor {
|
||||
return mColumns;
|
||||
}
|
||||
|
||||
private void deactivateCommon() {
|
||||
if (false) Log.v(TAG, "<<< Releasing cursor " + this);
|
||||
closeWindow();
|
||||
if (false) Log.v("DatabaseWindow", "closing window in release()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
deactivateCommon();
|
||||
mDriver.cursorDeactivated();
|
||||
}
|
||||
|
||||
@@ -217,6 +231,7 @@ public class SQLiteCursor extends AbstractWindowedCursor {
|
||||
public void close() {
|
||||
super.close();
|
||||
synchronized (this) {
|
||||
deactivateCommon();
|
||||
mQuery.close();
|
||||
mDriver.cursorClosed();
|
||||
}
|
||||
|
||||
@@ -21,12 +21,16 @@ import android.content.ContentProviderOperation;
|
||||
import android.content.ContentProviderResult;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.EntityIterator;
|
||||
import android.content.IContentProvider;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.content.pm.PathPermission;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWindow;
|
||||
import android.database.IBulkCursor;
|
||||
import android.database.IContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -51,75 +55,84 @@ public class MockContentProvider extends ContentProvider {
|
||||
* IContentProvider that directs all calls to this MockContentProvider.
|
||||
*/
|
||||
private class InversionIContentProvider implements IContentProvider {
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
|
||||
throws RemoteException, OperationApplicationException {
|
||||
return MockContentProvider.this.applyBatch(operations);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
|
||||
return MockContentProvider.this.bulkInsert(url, initialValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder, IContentObserver observer,
|
||||
CursorWindow window) throws RemoteException {
|
||||
throw new UnsupportedOperationException("Must not come here");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int delete(Uri url, String selection, String[] selectionArgs)
|
||||
throws RemoteException {
|
||||
return MockContentProvider.this.delete(url, selection, selectionArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public String getType(Uri url) throws RemoteException {
|
||||
return MockContentProvider.this.getType(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public Uri insert(Uri url, ContentValues initialValues) throws RemoteException {
|
||||
return MockContentProvider.this.insert(url, initialValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public AssetFileDescriptor openAssetFile(Uri url, String mode) throws RemoteException,
|
||||
FileNotFoundException {
|
||||
return MockContentProvider.this.openAssetFile(url, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public ParcelFileDescriptor openFile(Uri url, String mode) throws RemoteException,
|
||||
FileNotFoundException {
|
||||
return MockContentProvider.this.openFile(url, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) throws RemoteException {
|
||||
return MockContentProvider.this.query(url, projection, selection,
|
||||
selectionArgs, sortOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public int update(Uri url, ContentValues values, String selection, String[] selectionArgs)
|
||||
throws RemoteException {
|
||||
return MockContentProvider.this.update(url, values, selection, selectionArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public Bundle call(String method, String request, Bundle args)
|
||||
throws RemoteException {
|
||||
return MockContentProvider.this.call(method, request, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder asBinder() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
|
||||
return MockContentProvider.this.getStreamTypes(url, mimeTypeFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
|
||||
throws RemoteException, FileNotFoundException {
|
||||
return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
|
||||
|
||||
@@ -23,6 +23,9 @@ import android.content.EntityIterator;
|
||||
import android.content.IContentProvider;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWindow;
|
||||
import android.database.IBulkCursor;
|
||||
import android.database.IContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -44,6 +47,12 @@ public class MockIContentProvider implements IContentProvider {
|
||||
throw new UnsupportedOperationException("unimplemented mock method");
|
||||
}
|
||||
|
||||
public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder, IContentObserver observer,
|
||||
CursorWindow window) {
|
||||
throw new UnsupportedOperationException("unimplemented mock method");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int delete(Uri url, String selection, String[] selectionArgs)
|
||||
throws RemoteException {
|
||||
|
||||
@@ -23,6 +23,9 @@ import android.content.IContentProvider;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWindow;
|
||||
import android.database.IBulkCursor;
|
||||
import android.database.IContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -38,84 +41,78 @@ import java.util.ArrayList;
|
||||
* TODO: never return null when the method is not supposed to. Return fake data instead.
|
||||
*/
|
||||
public final class BridgeContentProvider implements IContentProvider {
|
||||
@Override
|
||||
|
||||
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> arg0)
|
||||
throws RemoteException, OperationApplicationException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bulkInsert(Uri arg0, ContentValues[] arg1) throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBulkCursor bulkQuery(Uri arg0, String[] arg1, String arg2, String[] arg3,
|
||||
String arg4, IContentObserver arg5, CursorWindow arg6) throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public Bundle call(String arg0, String arg1, Bundle arg2) throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri arg0, String arg1, String[] arg2) throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri arg0) throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri arg0, ContentValues arg1) throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetFileDescriptor openAssetFile(Uri arg0, String arg1) throws RemoteException,
|
||||
FileNotFoundException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri arg0, String arg1) throws RemoteException,
|
||||
FileNotFoundException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4)
|
||||
throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3)
|
||||
throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder asBinder() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getStreamTypes(Uri arg0, String arg1) throws RemoteException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetFileDescriptor openTypedAssetFile(Uri arg0, String arg1, Bundle arg2)
|
||||
throws RemoteException, FileNotFoundException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
Reference in New Issue
Block a user