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