Revert "Fix ownership of CursorWindows across processes."
This reverts commit d2183654e03d589b120467f4e98da1b178ceeadb.
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 092a0c8..a88220a 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -22,6 +22,10 @@
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 @@
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,
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 064755e..9a20951 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -20,7 +20,6 @@
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 @@
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 @@
}
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 @@
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();
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 2a67ff8..72bc9c2 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -18,6 +18,9 @@
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 @@
* @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;
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index ee6aec6..3f23b89 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -78,11 +78,13 @@
}
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 void close() {
mClosed = true;
mContentObservable.unregisterAll();
- onDeactivateOrClose();
+ deactivateInternal();
}
/**
diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java
index 5836265..bfc8123 100644
--- a/core/java/android/database/AbstractWindowedCursor.java
+++ b/core/java/android/database/AbstractWindowedCursor.java
@@ -19,11 +19,6 @@
/**
* 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 @@
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();
- }
}
diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java
index 4fada8c..9925a9a 100644
--- a/core/java/android/database/BulkCursorNative.java
+++ b/core/java/android/database/BulkCursorNative.java
@@ -62,13 +62,13 @@
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;
}
diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java
index cbdd07fb..9c1b26d 100644
--- a/core/java/android/database/BulkCursorToCursorAdaptor.java
+++ b/core/java/android/database/BulkCursorToCursorAdaptor.java
@@ -21,24 +21,40 @@
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 @@
*
* @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 @@
// 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 @@
@Override
public String[] getColumnNames() {
- throwIfCursorIsClosed();
-
if (mColumns == null) {
try {
mColumns = mBulkCursor.getColumnNames();
@@ -189,8 +187,6 @@
@Override
public Bundle getExtras() {
- throwIfCursorIsClosed();
-
try {
return mBulkCursor.getExtras();
} catch (RemoteException e) {
@@ -202,8 +198,6 @@
@Override
public Bundle respond(Bundle extras) {
- throwIfCursorIsClosed();
-
try {
return mBulkCursor.respond(extras);
} catch (RemoteException e) {
diff --git a/core/java/android/database/CrossProcessCursor.java b/core/java/android/database/CrossProcessCursor.java
index 8e6a5aa..77ba3a5 100644
--- a/core/java/android/database/CrossProcessCursor.java
+++ b/core/java/android/database/CrossProcessCursor.java
@@ -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
*/
diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java
index a65b3b3..8fa4d3b 100644
--- a/core/java/android/database/CursorToBulkCursorAdaptor.java
+++ b/core/java/android/database/CursorToBulkCursorAdaptor.java
@@ -24,37 +24,19 @@
/**
* 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 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 @@
+ 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 @@
* @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 @@
}
/** 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 @@
}
}
- @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);
}
}
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 5a91b80..2e3ef28 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -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 @@
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 @@
throw new CursorWindowAllocationException("Cursor window allocation of " +
(sCursorWindowSize / 1024) + " kb failed. " + printStats());
}
- mCloseGuard.open("close");
recordNewWindow(Binder.getCallingPid(), mWindowPtr);
}
@@ -107,15 +102,11 @@
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 @@
}
private void dispose() {
- if (mCloseGuard != null) {
- mCloseGuard.close();
- }
if (mWindowPtr != 0) {
recordClosingOfWindow(mWindowPtr);
nativeDispose(mWindowPtr);
@@ -689,10 +677,6 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeStrongBinder(nativeGetBinder(mWindowPtr));
dest.writeInt(mStartPos);
-
- if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
- releaseReference();
- }
}
@Override
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 9d7e152..81fe824 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -89,6 +89,8 @@
* @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 @@
}
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 @@
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 void close() {
super.close();
synchronized (this) {
+ deactivateCommon();
mQuery.close();
mDriver.cursorClosed();
}
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index e0ce322..b63ff3d 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -21,12 +21,16 @@
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 @@
* 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);
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index b7733a4..183be41 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -23,6 +23,9 @@
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 @@
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 {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index c91a3bf..3835378 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -23,6 +23,9 @@
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 @@
* 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