diff options
14 files changed, 310 insertions, 376 deletions
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 092a0c854f72..a88220a9a4cd 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -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, diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index 064755e95fbd..9a20951b8a99 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -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()); - - reply.writeNoException(); - reply.writeStrongBinder(binder); - reply.writeInt(count); - reply.writeInt(index); + // Flag for whether caller wants the number of + // rows in the cursor and the position of the + // "_id" column index (or -1 if non-existent) + // Only to be returned if binder != null. + boolean wantsCursorMetadata = data.readInt() != 0; + + IBulkCursor bulkCursor = bulkQuery(url, projection, selection, + selectionArgs, sortOrder, observer, window); + 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,70 +324,92 @@ 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); - - 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); + data.writeInterfaceToken(IContentProvider.descriptor); + + url.writeToParcel(data, 0); + int length = 0; + if (projection != null) { + length = projection.length; + } + data.writeInt(length); + for (int i = 0; i < length; i++) { + data.writeString(projection[i]); + } + data.writeString(selection); + if (selectionArgs != null) { + length = selectionArgs.length; + } else { + length = 0; + } + data.writeInt(length); + for (int i = 0; i < length; i++) { + data.writeString(selectionArgs[i]); + } + data.writeString(sortOrder); + data.writeStrongBinder(observer.asBinder()); + window.writeToParcel(data, 0); + + // Flag for whether or not we want the number of rows in the + // cursor and the position of the "_id" column index (or -1 if + // non-existent). Only to be returned if binder != null. + final boolean wantsCursorMetadata = (adaptor != null); + data.writeInt(wantsCursorMetadata ? 1 : 0); - mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); + mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); + DatabaseUtils.readExceptionFromParcel(reply); + + IBulkCursor bulkCursor = null; + IBinder bulkCursorBinder = reply.readStrongBinder(); + if (bulkCursorBinder != null) { + bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder); - IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder()); - if (bulkCursor != null) { + 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 diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index 2a67ff8dac54..72bc9c2f69f9 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -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; diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java index ee6aec6f0d2d..3f23b8900c5f 100644 --- a/core/java/android/database/AbstractCursor.java +++ b/core/java/android/database/AbstractCursor.java @@ -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(); } /** diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java index 5836265c4945..bfc8123e07fd 100644 --- a/core/java/android/database/AbstractWindowedCursor.java +++ b/core/java/android/database/AbstractWindowedCursor.java @@ -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(); - } } diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java index 4fada8c2fd29..9925a9ab7c0d 100644 --- a/core/java/android/database/BulkCursorNative.java +++ b/core/java/android/database/BulkCursorNative.java @@ -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; } diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index cbdd07fb29d3..9c1b26d0a648 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -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(); - - // 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(); + 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; } } 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) { diff --git a/core/java/android/database/CrossProcessCursor.java b/core/java/android/database/CrossProcessCursor.java index 8e6a5aa01598..77ba3a54463a 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 a65b3b304558..8fa4d3b5414d 100644 --- a/core/java/android/database/CursorToBulkCursorAdaptor.java +++ b/core/java/android/database/CursorToBulkCursorAdaptor.java @@ -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); - } + if (mWindow == null) { + ((AbstractWindowedCursor)mCursor).setWindow(window); + } + try { + if (!mCursor.requery()) { + return -1; } - - unregisterObserverProxyLocked(); - createAndRegisterObserverProxyLocked(observer); - return mCursor.getCount(); + } 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); } } diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 5a91b80034e4..2e3ef286fe80 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 @@ 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 diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java index 9d7e15201be5..81fe8241106a 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -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(); } diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java index e0ce3228c75b..b63ff3dca8df 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.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); diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java index b7733a4d55eb..183be415880c 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.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 { 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 c91a3bf91c51..3835378b5da0 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.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 |